標籤:

Python Generator漫談(續)

按, 上篇介紹了generator相關的概念. 這篇來探究一下generator相關的操作方法. 以下都是使用Python 2.7.10.

根據官方文檔 generator 通過 yield 來實現 next() 方法.

A function which returns a generator iterator. It looks like a normal function except that it contains yield expressions for producing a series of values usable in a for-loop or that can be retrieved one at a time with the next() function.

>>> def fib():n... a, b = 0, 1n... while True:n... yield bn... a, b = b, a + bn...n>>> fibn<function fib at 0x10d930c80>n>>> f = fib()n>>> fn<generator object fib at 0x10d92faa0>n>>> next(f)n1n>>> next(f)n1n>>> next(f)n2n>>> next(f)n3n>>> next(f)n5n

以上, 我們可以看到yield用法. 每次執行到yield, 都會記住執行狀態的局部變數以及表達式。再下次恢復時, 即next(), 它會從記住的狀態繼續執行.

generator的另一種調用方法是通過send(value)來實現的。我們來看一個例子:

>>> def gen():n... while True:n... value = yieldn... print(value)n...n>>> g = gen()n>>> g.send("ek")nTraceback (most recent call last):n File "<stdin>", line 1, in <module>nTypeError: cant send non-None value to a just-started generatorn

>>> next(g)n>>> g.send("ek")nekn>>> next(g)nNonen

以上, 展示了如何講value傳入yield當前表達式. 具體來說, 使用send(value)時, generator停在yield的語句. 傳入的值被複制到value, 然後print函數列印value, 經過循環遇到yield時, 暫停. 需要注意的是在沒有執行next()前, generator狀態並沒有停在yield狀態,也就無法傳入值.

>>> g = gen()n>>> g.send(None)n>>> g.send("ek")nekn

以上, 除了next(), 可以讓generator到達yield, send(None)作用是一樣的.

讓我們乘熱打鐵, 看一個稍微複雜的例子:

>>> def gen(value = None):n... while True:n... value = (yield value)n... print("The value is", value)n... if value:n... value += 1n...n>>> g = gen(1)n>>> next(g)n1n>>> g.send(2)n(The value is, 2)n3n>>> g.send(10)n(The value is, 10)n11n>>> next(g)n(The value is, None)n

在這個例子里, value = (yield value) 這個形式看起來很複雜. 但兩個value 的含義並不相同. 具體來看, 執行next()時, generator執行到yield value表達式, 保存上下文環境暫停返回當前值1. 再執行send(value)時, 從value = yield開始, 列印傳入的值 2, 再次遇到yield value暫停返回當前值 3 (上個循環時 加上 1). send(10)是一樣的. 在看最後一個next(), 這裡需要記住的是調用next()表達式的值時, yield的值總是為None. 因此, 在這裡返回None.

接下來, 我們來實現一下Python 內建函數 range() 實現方法 (復刻版, 非真實實現):

>>> def my_range(start, stop, step = 1):n... if stop <= start:n... raise RuntimeError("Start must be smaller than stop")n... i = startn... while i < stop:n... yield in... i += stepn...n>>> try:n... for k in my_range(10, 50, 3):n... print(k)n... except RuntimeError as ex:n... print(ex)n... except:n... print("Unknown error occurred")n...n10n13n16n19n22n25n28n31n34n37n40n43n46n49n

最後, generator 的另外兩個方法分別是 throw(type[, value[, traceback]]) 和 close(). 前者用於拋出 type 異常, 後者用於關閉generator. 我們直接看下Python文檔的例子:

>>> def echo(value=None):n... print("Execution starts when next() is called for the first time.")n... try:n... while True:n... try:n... value = (yield value)n... except Exception as e:n... value = en... finally:n... print("Dont forget to clean up when close() is called.")n...n>>> generator = echo(1)n>>> print(next(generator))nExecution starts when next() is called for the first time.n1n>>> print(next(generator))nNonen>>> print(generator.send(2))n2n>>> generator.throw(TypeError, "spam")nTypeError(spam,)n>>> generator.close()nDont forget to clean up when close() is called.n

歡迎訪問Chen Tong的博客原文鏈接

本文作者: EK,更多內容,請訪問:BitTiger.io, 掃描下面二維碼,關注微信公眾賬號「論碼農的自我修養」

推薦閱讀:

TAG:Python |