本文挑選了 StackOverflow 上被點贊最多的10個問題,其中總點贊數超過了5萬,考慮到很多人只看不點贊,預估至少有10萬人對這些問題感興趣!
這麽多人點贊,說明兩個問題:
這些問題很常用,編程的時候經常碰到
這些問題不簡單,否則不用去網上問
10個問題,看看你會幾個?
yield 關鍵詞是做什麽的?
if name == ' main ' 是做什麽的 ?
Python 有三元運算子嗎?
Python 的 meta classes 是做什麽的?
如果在不出異常的情況下檢查檔是否存在?
如何一句話合並兩個字典?
Python 如何呼叫外部命令,比如啟動QQ?
如何安全的建立一個多層資料夾?
迴圈中如何存取下標?
staticmethod 和 classmethod 的區別?
這10個問題,有的復雜,有的簡單。你會幾個呢?可以在評論區留言。
我原本打算講解10個問題,但由於篇幅原因,本文只涵蓋了被問最多的一個問題,後續文章可能會涵蓋多個問題。
下面我們重點看第一個問題:
yield關鍵詞是做什麽的?
這個問題是所有Python問題的排名第一:
有10000多人對問題點贊,表示有同樣疑問。
其中高贊回答有15000多點贊。
有超過2百40萬的瀏覽。
問題詳細內容?
Yield關鍵詞是做什麽的?
比如下面的程式碼:
def_get_child_candidates(self, distance, min_dist, max_dist):
if self._leftchild and distance - max_dist < self._median:
yield self._leftchild
if self._rightchild and distance + max_dist >= self._median:
yield self._rightchild
這是呼叫的程式碼:
result, candidates = [], [self]
while candidates:
node = candidates.pop()
distance = node._get_dist(obj)
if distance <= max_dist and distance >= min_dist:
result.extend(node._values)
candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
return result
當_get_child_candidates被呼叫時,發生了什麽?返回了一個list嗎?還是一個元素?它會被反復調嗎?後續呼叫什麽時候停止?
看著有點懵?可以繼續往下看解答,然後再回來看問題。
最高贊回答 (15000多贊)
要理解yield,先理解generators,要理解generators先理解iterable(可叠代的)。
iterables
當你建立1個list,你可以一個個讀取它的值,這叫做叠代:
>>> mylist = [1, 2, 3]
>>> for i in mylist:
... print(i)
1
2
3
上面mylist是一個iterable(可以被叠代的)。當你使用一個列表推導式,你建立了一個列表,也就是一個iterable:
>>> mylist = [x*x for x in range(3)]
>>> for i in mylist:
... print(i)
0
1
4
所以可以使用for...in...語法遍歷的就是iterable:list, str, file等等
這些iterable很有用,你可以迴圈存取他們。但是它們所有的值都保存在記憶體中。如果你的list中有10億個字串的時候,建立這個list會很慢,而且會很占用記憶體,所以我們需要genertor.
generators
generator是iterable,可以被迴圈。但和上面不一樣,它一般只能被迴圈一次。它不會把所有的值保存在記憶體中,他們在迴圈中動態產生元素的值。
>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
... print(i)
0
1
4
這個生成器和列表推導式幾乎一樣,唯一區別使用小括弧(),而不是中括弧[]. 但,你不能兩次使用for i in mygenerator,因為生成器只能被迴圈一次:他們計算0x 0,返回結果,自己並不保存,下一次呼叫它,他計算1x 1,以此類推。
yield
yields是一個關鍵詞,可以先理解成和return一樣,區別是它返回一個generator.
>>> defcreateGenerator():
... mylist = range(3)
... for i in mylist:
... yield i*i
...
>>> mygenerator = createGenerator() # create a generator
>>> print(mygenerator) # mygenerator is an object!
<generator object createGenerator at 0xb7555c34>
>>> for i in mygenerator:
... print(i)
0
1
4
上面這個例子先建立了range,已經占用了記憶體,但平方數沒有占用。這個例子不是很好,這是原作者舉的,我的視訊中應該有更好的例子。
首先,因為yield的存在,當你呼叫上面的函式,裏面的程式碼並沒有執行,而不是返回了一個generator。
然後是關鍵的地方:
當for迴圈第一次呼叫generator的時候,它會從頭開始執行,直到yield關鍵詞,返回第一個值,也就是0。
然後記住執行到哪一行程式碼,也就是yield的位置。
下次for迴圈再次呼叫它,它從yield的下一行繼續執行,直到再次碰到yield,返回下一個值,也就是1.
這個過程一直重復,直到generator中沒有內容了。例子中就是range裏的數位被用完。
現在再看看最開始的問題:
這是一個對樹的遍歷演算法,尋找樹上符合條件的節點。程式碼中加了詳細的中文註釋。
generator:
# 這個函式會返回一個生成器(Generator),這是一個二元樹的node物件中的方法
def_get_child_candidates(self, distance, min_dist, max_dist):
# 如果還有左孩子,並且距離符合條件,返回左孩子,然後暫停在這裏
if self._leftchild and distance - max_dist < self._median:
yield self._leftchild
# 如果還有右孩子,並且距離符合條件,返回右孩子,然後暫停在這裏
if self._rightchild and distance + max_dist >= self._median:
yield self._rightchild
# 如果執行到了這裏,說明沒有符合條件的左右孩子了,生成器空了,就叠代結束了。
caller:
# 建立一個空的列表,和當前物件節點
result, candidates = list(), [self]
# 迴圈,開始裏面只有自己
while candidates:
# 彈出最後一個節點
node = candidates.pop()
# 獲得obj物件和目標節點的距離
distance = node._get_dist(obj)
# 如果距離ok,寫入到結果列表中
if distance <= max_dist and distance >= min_dist:
result.extend(node._values)
# 把自己的子節點加入到candidates中,這樣迴圈會繼續,直到樹上的所有節點都被遍歷
candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
return result
作者:
麥叔
來源 :麥叔編程
Crossin的新書【 碼上行動:用ChatGPT學會Python編程 】已經上市了。 本書以ChatGPT為輔助,系統全面地講解了如何掌握Python編程,適合Python零基礎入門的讀者學習。
購買後可加入讀者交流群,Crossin為你開啟陪讀模式,解答你在閱讀本書時的一切疑問。
Crossin的其他書籍:
添加微信 crossin123 ,加入編程教室共同學習 ~
感謝 轉發 和 點贊 的各位~