生成器和迭代器
生成器
在上文中簡單介紹了生成器的作用。在列表推導(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 2 和 2,直到?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)注微信公眾號《書所集錄》