1. 程式人生 > 實用技巧 >【Python】迭代器和生成器的個人理解,再講一講協程

【Python】迭代器和生成器的個人理解,再講一講協程

  在認識yield的時候,網上很多文章都是說這個是個生成器,但是我並不知道這個是用來做什麼的,所以概念很快就忘記了,後面讀了幾個文章以後感覺茅塞頓開。我就接介紹一下。

  有一篇文章提到,可以把yield看成是生成器的return的一部分,首先一個return的作用是在程式中返回某個值,返回之後程式就不再往下執行了,那麼生成器是是什麼,只有呼叫next()方法的時候該函式才會執行。結合來看,當一個函式帶有yield,它已經不是一個函數了,而是一個生成器,即一個返回迭代器的函式,返回的迭代器有一個next方法,但是每次走一步,到yield停一次,下一次執行到next()時,再從上一次暫停的位置開始,直到執行到下一個yield表示式,將yield關鍵字後的表示式列表返回給呼叫者,並再次暫停。

  舉個例子:

def run():
    print("starting...")
    while True:
        res = yield 1
        print("res:",res)

g = run()
print(next(g))
print("------")
print(next(g))

輸入結果如下:
starting...
1
------
res: None
1

  上面的表達有點繞口,但是如果你懂迭代器,那麼在這裡你就懂了。如果不懂,也沒關係,我將一下迭代器。

  迭代器的功能主要用於訪問集合元素,迭代器從集合的第一個元素開始訪問,知道所有元素被訪問完結束。向我們的list,tuple,string,dict,都是可以迭代的,對於我們自己實現的型別,如果實現了__iter__()或者__getitem__()方法,那麼該類物件也是可以迭代的。

  抽象來看的話,迭代器是一個數據流,對迭代器不斷呼叫next()方法,就可以依次獲取下一個元素,當迭代器沒有元素的時候,呼叫next()會丟擲StopIteration異常。iter()方法則返回一個特殊的迭代物件,當出現StopIteration異常的時候,則識別迭代完成結束。最常見的就是我們的for迴圈:

for i in range li:
    print(i)

  Python 處理for迴圈時,首先會呼叫內建函式 iter(li),它實際上會呼叫 li.__iter__(),返回 li對應的迭代器。而後,li迴圈會呼叫內建函式next(),作用在迭代器上,獲取迭代器的下一個元素,並賦值給x

。這個時候,Python 才算是真正開始執行迴圈體。

  那問題來了,我們知道迭代器的用法,那生成器在什麼地方可以用呢。那就不得不提到協程了。協程也叫微執行緒,舉個例子:

  函式的呼叫都是層級呼叫,抽象來看是實現了棧的呼叫,a呼叫b,b呼叫c,那麼c執行完畢返回,再b執行完畢返回,再a執行完畢。這裡的呼叫順序是明確的的。

  但是協程不同,雖然它也是子程式,但是在執行的過程中,子程式內部會發生中斷,轉而去執行別的子程式,在適當的時候再跳回來。

  那好處在哪裡呢,執行緒的切換是需要開銷的,而子程式的切換由程式自己控制,效能優勢就有了。另外一個是因為只有一個執行緒,不存在讀寫衝突,在控制共享資源的時候不加鎖,優勢就更大了。

  正如前面所說的,yield能提供一個函式執行過程中的暫停,這個協程的子程式內部中斷的思想不謀而合,如果使用協程去寫生產者-消費者模型,那麼當生產者生產訊息以後,直接通過yield跳轉到消費者開始執行,待消費者執行完畢以後,切換回生產者繼續生產,效率極高:

import time

def consumer():
    r = ''
    while True:
        n = yield r
        if not n:
            return
        print('[CONSUMER] Consuming %s...' % n)
        time.sleep(1)
        r = '200 OK'

def produce(c):
    c.next()
    n = 0
    while n < 5:
        n = n + 1
        print('[PRODUCER] Producing %s...' % n)
        r = c.send(n)
        print('[PRODUCER] Consumer return: %s' % r)
    c.close()

if __name__=='__main__':
    c = consumer()
    produce(c)