深入理解Python中yield關鍵字

閱讀別人的python源碼時碰到了這個yield這個關鍵字,各種搜索終於搞懂了,在此做一下總結:

  1. 通常的for…in…循環中,in後面是一個數組,這個數組就是一個可迭代對象,類似的還有鏈表,字符串,文件。它可以是mylist 
    = [1, 2, 3],也可以是mylist = [x*x for x in range(3)]。 它的缺陷是所有數據都在內存中,如果有海量數據的話將會非常耗內存。
  2. 生成器是可以迭代的,但只可以讀取它一次。因爲用的時候才生成。比如 mygenerator = (x*x for x in 
    range(3)),注意這裏用到了(),它就不是數組,而上面的例子是[]。
  3. 我理解的生成器(generator)能夠迭代的關鍵是它有一個next()方法,工作原理就是通過重複調用next()方法,直到捕獲一個異常。可以用上面的mygenerator測試。
  4. 帶有 yield 的函數不再是一個普通函數,而是一個生成器generator,可用於迭代,工作原理同上。
  5. yield 是一個類似 return 
    的關鍵字,迭代一次遇到yield時就返回yield後面的值。重點是:下一次迭代時,從上一次迭代遇到的yield後面的代碼開始執行。
  6. 簡要理解:yield就是 return 返回一個值,並且記住這個返回的位置,下次迭代就從這個位置後開始。
  7. 帶有yield的函數不僅僅只用於for循環中,而且可用於某個函數的參數,只要這個函數的參數允許迭代參數。比如array.extend函數,它的原型是array.extend(iterable)。
  8. send(msg)與next()的區別在於send可以傳遞參數給yield表達式,這時傳遞的參數會作爲yield表達式的值,而yield的參數是返回給調用者的值。——換句話說,就是send可以強行修改上一個yield表達式值。比如函數中有一個yield賦值,a 
    = yield 5,第一次迭代到這裏會返回5,a還沒有賦值。第二次迭代時,使用.send(10),那麼,就是強行修改yield 5表達式的值爲10,本來是5的,那麼a=10
  9. send(msg)與next()都有返回值,它們的返回值是當前迭代遇到yield時,yield後面表達式的值,其實就是當前迭代中yield後面的參數。
  10. 第一次調用時必須先next()或send(None),否則會報錯,send後之所以爲None是因爲這時候沒有上一個yield(根據第8條)。可以認爲,next()等同於send(None)。

代碼示例1:

#encoding:UTF-8  
def yield_test(n):  
    for i in range(n):  
        yield call(i)  
        print("i=",i)  
    #做一些其它的事情      
    print("do something.")      
    print("end.")  

def call(i):  
    return i*2  

#使用for循環  
for i in yield_test(5):  
    print(i,",")

結果是:

>>>   
0 ,  
i= 0  
2 ,  
i= 1  
4 ,  
i= 2  
6 ,  
i= 3  
8 ,  
i= 4  
do something.  
end.  
>>>

理解的關鍵在於:下次迭代時,代碼從yield的下一跳語句開始執行。 
for循環就用到了next(),所以到yield能再執行

代碼示例2:

def node._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

與前面不同的是,這個函數中沒有for循環,但它依然可以用於迭代。 
node._get_child_candidates函數中有yield,所以它變成了一個迭代器,可以用於迭代。 
執行第一次迭代時(其實就是調用next()方法),如果有左節點並且距離滿足要求,會執行第一個yield,這時會返回self._leftchild並完成第一個迭代。 
執行第二次迭代時,從第一個yield後面開始,如果有右節點並且距離滿足要求,會執行第二個yield,這時會返回self._rightchild並完成第一個迭代。 
執行第三次迭代時,第二個yield後再無代碼,捕獲異常,退出迭代。

調用過程:

result, candidates = list(), [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

上面的node._get_child_candidates(self, distance, min_dist, max_dist)是放在extend()函數中作爲參數的,爲什麼可以這麼用,就因爲extend函數的參數不僅僅支持array,只要它是一個迭代器就可以。它的原型是array.extend(iterable)。

代碼示例3:

send()修改上一個yield掃完返回值

這裏寫圖片描述

轉自:http://www.jianshu.com/p/d09778f4e055

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章