Python 生成器和迭代器

生成器和迭代器


生成器


Python 列表推導(dǎo)和生成器表達(dá)式

在上文中簡單介紹了生成器的作用。在列表推導(dǎo)創(chuàng)建列表時,受到內(nèi)存的限制,列表容量同樣會受到限制,而且會極大的消耗空間。這時,一邊循環(huán)一邊計算這種機(jī)制,也就是生成器也就出現(xiàn)了。

創(chuàng)建生成器

創(chuàng)建生成器(generator),有很多方法。其中一種,使用生成器表達(dá)式,即是將列表推導(dǎo)中的方括號 [] 改成圓括號 ()。

如下示例:

>>> L = [x * x for x in range(10)] 
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10)) 
>>> g
<generator object <genexpr> at 0x000001FBFD43A048>
>>> list(g)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

由于生成器返回的結(jié)果是惰性序列,并不會直接返回想要結(jié)果,可以手動遍歷,一個個進(jìn)行打?。?/p>

>>> g = (x * x for x in range(10))
>>> next(g) 
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
36
>>> next(g)
49
>>> next(g)
64
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

調(diào)用 next() 可以返回 g 的下一個元素值,當(dāng)沒有更多元素的時候,會拋出 StopIteration 的錯誤。若只是遍歷獲得結(jié)果,可以使用 for 循環(huán),因為 generator 是可迭代對象:

>>> g = (x * x for x in range(10))
>>> for i in g: 
...     print(i) 
... 
0
1
4
9
16
25
36
49
64
81

使用 for 循環(huán)遍歷的時,就不需要擔(dān)心 StopIteration 異常。

還有另外一種方法可以創(chuàng)建生成器(generator),在 Python 中,使用 yield 的函數(shù)被稱為生成器(generator)函數(shù)。使用 yield 實現(xiàn)斐波那契數(shù)列,如下示例:

def fib(n):
    ct, a, b = 0, 0, 1
    while ct < n:
        yield b
        a, b = b, a + b
        ct += 1
    return "done"

生成器和普通函數(shù)執(zhí)行過程有所不同。函數(shù)是順序執(zhí)行,遇到 return 會返回結(jié)果。而生成器遇到 next() 會執(zhí)行,遇到 yield 返回,再次執(zhí)行會直接在上次返回的 yield 繼續(xù)執(zhí)行。如下示例:

>>> def test():
...     print("value 1")
...     yield(1)
...     print("value 2")
...     yield(2)
...     print("value 3")
...     yield(3)
... 
>>> t = test()
>>> next(t)
value 1
1
>>> next(t)
value 2
2
>>> next(t)
value 3
3
>>> next(t)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

上述代碼中,調(diào)用 next() 后,打印 value 1,遇到 yield 輸出 1,停止執(zhí)行,再次執(zhí)行 next() 時,在上次遇到 yield 語句停止的后面繼續(xù)執(zhí)行,輸出 value 22,直到?jīng)]有元素,拋出異常。這就是生成器函數(shù)的執(zhí)行過程。

迭代器


開始迭代器的內(nèi)容前,先延伸介紹一個可迭代對象的概念??梢灾苯幼饔糜?for 循環(huán)的對象稱為可迭代對象:Iterable ??梢杂?isinstance() 進(jìn)行判斷:

>>> from collections import Iterable
>>> isinstance('abc', Iterable) 
True
>>> isinstance((x for x in range(10)), Iterable) 
True
>>> isinstance(100, Iterable)
False

可以被 next() 函數(shù)調(diào)用并不斷返回下一個值得對象稱為迭代器:Iterator。同樣可以用 isinstance() 判斷對象是否是 Iterator 對象:

>>> from collections import Iterator 
>>> isinstance('abc', Iterator)
False
>>> isinstance((x for x in range(10)), Iterator) 
True

可以看出,生成器也是 Iterator 對象。但 str 雖然是 Iterable,卻不是 Iterator,同樣的還有列表 list,字典 dict

iter() 函數(shù)能夠?qū)?Iterable 變成 Iterator,例如:

>>> isinstance(iter('abc'), Iterator) 
True
>>> isinstance(iter([]), Iterator)    
True

創(chuàng)建一個迭代器

創(chuàng)建迭代器需要實現(xiàn)兩個方法:__iter__()__next__()

__iter__() 方法返回一個特殊的迭代器對象,這個對象能夠?qū)崿F(xiàn) __next__() 方法通過 StopIteration 異常標(biāo)識迭代的完成。

__next__() 方法會返回下一個迭代器對象。

實現(xiàn)逐步加 1 的類,示例如下:

>>> class NumberIncrease(): 
...     def __iter__(self): 
...         self.x = 1      
...         return self     
...     def __next__(self): 
...         i = self.x      
...         self.x += 1
...         return i
... 
>>> num_increase = NumberIncrease()
>>> num_iter = iter(num_increase)
>>> next(num_iter)
1
>>> next(num_iter)
2
>>> next(num_iter)
3
>>> next(num_iter)
4
>>> next(num_iter)
5

以上就是本篇的主要內(nèi)容


歡迎關(guān)注微信公眾號《書所集錄》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容