Python生成器next方法和send方法區別

bingo發表於2019-02-16

生成器簡介

python中,含有yield關鍵字的物件就是一個生成器,每次呼叫next方法時會執行到yield後面的語句,然後返回yield後面程式碼塊的執行結果。其實也可以呼叫send方法
下面給個例子方便理解。

next方法

def foo():
    bar_a = yield 1         # bar_a是語句塊(yield 1)的返回值,預設為None
    bar_b = yield bar_a
    yield "最後一個值,再迭代就要報StopIteration了"

f = foo()                   # 建立生成器,此時沒有執行foo()裡的任何語句
print(next(f))              # 從foo()裡進入,一直執行到(yield 1)處,此時變數bar_a還沒有建立
print(next(f))              # 先將語句塊(yield 1)的返回值賦值個bar_a,此時bar_a的值是None。
                            # 然後執行到語句塊(yield bar_a),bar_b也還沒有被建立
print(next(f)

輸出:

>>>1
>>>None
>>>最後一個值,再迭代就要報StopIteration了

可以看出,f = foo()建立生成器時,每次執行到yield時,會跳出去並將yield關鍵字後面的內容返回給呼叫者。下一次有別的呼叫者再次呼叫生成器時,會先恢復生成器上次的機器狀態,再接著執行指導遇到yield或者元素迭代完畢。
而且我們可以看到bar_abar_b是語句yield 1yield bar_a的返回值,注意:不是生成器的返回值
這裡有個比較繞的地方,我們用bar_a = yield 1做分析:

  • 1是生成器的返回值。因為生成器返回yield後面的程式碼塊
  • bar_a是語句yield 1的返回值,這就好比我們寫

    a = print(`my lover`)
    print(`a的值是:`, a)

    會輸出:

    >>>my lover
    >>>a的值是: None
    

send方法

def foo():
    bar_a = yield 1
    bar_b = yield bar_a
    yield "最後一個值,再迭代就要報StopIteration了"

f = foo()
print(f.send(None))
print(f.send("my lover"))
print(next(f))

輸出:

>>>1
>>>my lover
>>>最後一個值,再迭代就要報StopIteration了

這裡f.send(None)是初始化生成器,和next(f)的效果一模一樣。但是不推薦這麼寫,因為不規範。
注意輸出的第二行是字串my lover,而不是None。這是因為send函式帶有一個引數,這個引數會覆蓋yield 1語句的返回值,也就是bar_a的值現在不是None了。

FAQ

官網提到,當我們建立一個生成器時,第一次呼叫只能用next()或者send(None)。因為此時send傳入其他引數也沒有yield語句去接收。
這句話我看不懂,說的好像傳入None就有yield來接收似的。各位如果明白的歡迎指點。
原文和傳送門如下:

官網傳送門

Resumes the execution and “sends” a value into the generator function. The value argument becomes the result of the current yield expression. The send() method returns the next value yielded by the generator, or raises StopIteration if the generator exits without yielding another value. When send() is called to start the generator, it must be called with None as the argument, because there is no yield expression that could receive the value.

Google翻譯版本:
恢復執行並將值“傳送”到生成器函式中。所述 值引數成為當前產量表達的結果。該 send()方法返回由生成器產生的下一個值,或者StopIteration如果生成器退出而不產生另一個值則引發。當send()呼叫啟動生成器時,必須將其None作為引數呼叫,因為沒有可以接收該值的yield表示式。


更新:
2018-11-24
對生成器使用send(None)方法,直譯器在底層會呼叫__next__方法,也就是next()方法

相關文章