生成器簡介
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_a
和bar_b
是語句yield 1
和yield 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()
方法