python generator_Python Generator漫谈
作為一個Python初學者, Python的格式化語法讓眾多編程小白追捧, 它的語法糖讓代碼變得簡潔易讀,它的龐大開源庫讓它在各個領域都能發揮作用. 但我時常感受到這個門檻極低的語言遠沒有表面上看起來易懂易用. 在Python的學習之路上, 我也時常迷茫于自己是否真正掌握了這門語言. Stackoverflow上有一篇帖子很好地說明了Python的進階之路, 于是我準備順著這個思路寫寫各個要點.
根據這篇帖子, Python進階之路: 從小白到大神. 其中, 前兩點分別是Discover list comprehensions 和 Discover generators(生成器). 身為小白的我, 心里暗想generators是個啥。于是,研究了一番,遂成此文。
注: 全文使用Python3.5, 標準庫.
談起Generator, 與之相關的的概念有{list, set, tuple, dict} comprehension and container
iterable
iterator
generator fuction and iterator
generator expression
接下來, 我們分別來看看這些概念:
{list, set, tuple, dict} comprehension and container
Container是存儲元素的數據結構, 一般存儲在內存中. 在Python中,常見的container包括但不限于:list, deque, …
set, …
tuple, namedtuple, …
dict, defaultdict, Counter, …
簡單舉例:
assert 1 in [1, 2, 3] # list
assert 1 in {1, 2, 3} # set
assert 1 in (1, 2, 3) # tuple
d = {1: 'foo', 2: 'bar', 3: 'qux'}
assert 1 in d # dict
特別的, String也是container. 我們可以查詢一個substring是否在string里, 如:
s = 'foobar'
assert 'foo' in s
assert 'x' not in s # string 包含所有的 substrings. 需要注意的是string并不存儲這些substrings, 只是可以如此查詢
iterable
一般說來,大部分的containers是iterable.而iterable不限于containers,還包括像文件.
iterable是實現了__iter__()方法的對象.該方法返回的是的一個iterator對象.其中,iterator的目的是返回所有元素.來看例子:
from collections import Iterable, Iterator
x = [1, 2, 3]
y = iter(x)
z = iter(x)
next(y)
Out: 1
next(y)
Out: 2
next(z)
Out: 1
next(z)
Out: 2
type(x)
Out: list
isinstance(x, Iterable)
Out: True
type(y)
Out: list_iterator
isinstance(y, Iterable)
Out: True
可以看出, x是list, 也是iterable. y和z都是x的返回值, 也就是iterators, 互相獨立. iterators通過___next___()這個方法返回元素,每執行一次, 返回一個元素. 值得注意的是,y作為iterable的實例, 它也是iterator. iterable是一個比iterator更大的概念.
import dis
x = [1, 2, 3]
dis.dis('for _ in x: pass')
1 0 SETUP_LOOP 14 (to 17)
3 LOAD_NAME 0 (x)
6 GET_ITER
>> 7 FOR_ITER 6 (to 16)
10 STORE_NAME 1 (_)
13 JUMP_ABSOLUTE 7
>> 16 POP_BLOCK
>> 17 LOAD_CONST 0 (None)
20 RETURN_VALUE
更深入的, 我們可以明顯看出在for循環中, Python每次都是調用FOR_ITER這個指令實現了讀取下一個next()的功能, 也就是說, for循環中,首先利用GET_ITER得到x的返回值, 一個iterator. 再通過FOR_ITER得到其中的元素. 如此實現了循環的功能.
iterator
那么什么是iterator? 就像之前我們提到的, iterator可以通過調用__next___()生成下一個值. 我們也可以說有__next___()這個方法的都可以是iterator. 這與它如何生成值無關. 我們具體看一個例子:
from itertools import islice
class seq:
def __init__(self):
self.gap = 2
self.curr = 1
def __iter__(self):
return self
def __next__(self):
value = self.curr
self.curr += self.gap
return value
f = seq()
list(islice(f, 0, 10))
Out: [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
以上, 我們構建了一個等差數列, 差為2. 其中, f既是iterable(因為iter方法), 也是iterator(因為有next方法).
對于iterator, next方法做的兩件事是:更新iterator的狀態,到下次調用;
返回當前調用結果, eg: return value.
generator function and iterator
Ok. 終于到了本文的正題 generator. 首先下定義, generator是返回generator iterator的function(函數). 具體來說, 它可以讓你更優雅的實現iterator, 而不用寫帶有__iter__() 和 __next__() 的類. 繼續以上面那個等差數列為例, 我們看看generator如何的優雅的完成它:
def seq():
gap, curr = 2, 1
while True:
yield curr
curr = curr + gap
f = seq()
list(islice(f, 0, 10))
Out: [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
以上, 我們看到了讓它如此優雅的原因 yield.
讓我們看看這段code是如何運行的,
首先, seq是一個Python程序, 它返回的是iterator. 當 f = seq() 被調用, generator 會準備好返回. 但這時沒有執行任何代碼.
接下來, 這個generator實例在islice()中被使用. 仍然沒有執行代碼. 然后, list()開始建立list. 為了得到list的元素, list()開始調用islice()的next()方法抓取元素, 也就是要在 f 函數中取元素.
記住每次只生成一個元素. 這是代碼開始運行到 gap, curr = 2, 1 初始化變量. 接著進入始終為真的循環, 到了yield語句. 它做兩件事: 1.暫停, 并更新狀態到下一次yield, 2. 返回當前結果, 也就是curr值, 1. 這個值接著被傳到了islice(), 最后加到list中. list現在是 [1].
list()繼續要下一個值. 暫停的 f 函數繼續運行下一個語句curr + gap. curr 現在是2. 繼續進入下一個while循環, 遇到 yield. 同樣的, yield做兩件事: 1.暫停下來調整狀態; 2. 返回值2. 值返回到list, list 等于 [1, 2].
如此循環直到list取完10個元素. 特別的, 在第11次去值, islice()會有exception: StopIteration. 告訴list已經取值結束.
generator expression
generator expression是Python的另一種generator. 相信大家都用過list expression, 比如生成一列數的平方:
numbers = [1, 2, 3, 4, 5, 6]
[x ** 2 for x in numbers]
[1, 4, 9, 16, 25, 36]
generator expression很類似, 比如
squares_list = (x * x for x in numbers)
squares_list
at 0x10435eaf0>
next(squares_list)
Out: 1
list(squares_list)
Out: [4, 9, 16, 25, 36]
以上, 不再累述.
Summary
下圖說明了各個概念之間的關系,
最后,談談為什么要使用generators. 它能幫你實現更優雅的代碼, 減少中間變量和不必要的數據結構,從而代碼量, 節省內存空間和運算性能.
針對一個循環代碼,
def something():
result = []
for ... in ...:
result.append(x)
return result
你可以做這樣的轉換
def iter_something():
for ... in ...:
yield x
感謝閱讀,歡迎大家修改指正.
本文作者:Early Kid
更多內容,請訪問:BitTiger.io, 掃描下面二維碼,關注微信公眾賬號“論碼農的自我修養”
總結
以上是生活随笔為你收集整理的python generator_Python Generator漫谈的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 江南百景图应天府地图布局规划攻略
- 下一篇: 江湖悠悠孔雀翎怎么用 孔雀翎有什么用