1. 程式人生 > >python yield 與 協程的實現

python yield 與 協程的實現

今天早上偶然看到一篇文章《PHP如何實現協程》,頓時驚呆了!PHP什麼時候這麼強了?那我還寫Go 做什麼?仔細閱讀文章發現php使用的是yield關鍵實現的,心想這個不就是py裡的生成器嗎?雖然我不知道PHP裡的yield和py的有什麼區別,由於之前並沒有深入瞭解過著玩意所以藉機會了解一下!

我們都知道函式(子例程)的控制權要等到return 後才會交給呼叫者,函式中的變數隨著控制權結束後也就消失了,需要等到下次呼叫重新開始,然而yield 就是這麼神奇可以執行到yield 關鍵字處然後將控制權交給呼叫者,然後在次進入函式從上次結束的地方繼續執行不斷迴圈

先從一個小需求入手:

求出一個數字列表中的素數

import math
def get_primes(input_list):
    return (element for element in input_list if is_prime(element))


def is_prime(number):
    if number > 1:
        if number == 2:
            return True
        if number % 2 == 0:
            return False
        for current in range(3,int(math.sqrt(number)+1),2):
            if number % current == 0:
                return False
        return True
    return False
nums  = [1,2,3,4,5,6,7,8,9]
list = get_primes(nums)
for i in list:
    print(i)

這個需求很簡單就實現了~~·

假如現在萬惡的產品經理想要求無限大的素數

那麼你可能會想我直接從一個非常的大的數開始算不行麼?可以的!但是就是有點LOW

如果我們往list存無限個數字 就更加不可能了

這是我們的yield就非常有作用了,上面說了遇到yield 會將交出控制權,然後進入函式繼續執行

這裡我們改造一下get_primes 函式


def get_primes_v2(number):
    while True:
        time.sleep(2)
        print("*******", number)
        if is_prime(number):
            yield number
        number+=1

使用while True不停的去執行並且每次到yield 關鍵字處就將結果返回並且儲存函式的內部的狀
態,實現每次number+1 的效果這就是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("send.......")
        print('[PRODUCER] Consumer return: %s' % r)
    c.close()


c = consumer()
produce(c)

注意到consumer函式是一個generator(生成器),把一個consumer傳入produce後:

首先呼叫c.next()啟動生成器;

然後,一旦生產了東西,通過c.send(n)切換到consumer執行;

consumer通過yield拿到訊息,處理,又通過yield把結果傳回;

produce拿到consumer處理的結果,繼續生產下一條訊息;

produce決定不生產了,通過c.close()關閉consumer,整個過程結束。

整個流程無鎖,由一個執行緒執行,produce和consumer協作完成任務,所以稱為“協程”,而非執行緒的搶佔式多工。

其實通過yield 也知道go 中goroutine 翻譯成協程還是有很大的不同的,goroutine 和協程是根本上不同的東西,之後再探討go 中的goroutine 是如何實現的