|
來源:Yushneng 鏈接:https://github.com/rainyear/pytips

項(xiàng)目地址:https:///pytips
迭代器與生成器
迭代器(iterator)與生成器(generator)是 Python 中比較常用又很容易混淆的兩個(gè)概念,今天就把它們梳理一遍,并舉一些常用的例子。
for 語句與可迭代對象(iterable object):
for i in [1, 2, 3]: print(i)
1 2 3
obj = {'a': 123, 'b': 456} for k in obj: print(k)
b a
這些可以用在 for 語句進(jìn)行循環(huán)的對象就是可迭代對象。除了內(nèi)置的數(shù)據(jù)類型(列表、元組、字符串、字典等)可以通過 for 語句進(jìn)行迭代,我們也可以自己創(chuàng)建一個(gè)容器,包含一系列元素,可以通過 for 語句依次循環(huán)取出每一個(gè)元素,這種容器就是迭代器(iterator)。除了用 for 遍歷,迭代器還可以通過 next() 方法逐一讀取下一個(gè)元素。要?jiǎng)?chuàng)建一個(gè)迭代器有3種方法,其中前兩種分別是:
為容器對象添加 __iter__() 和 __next__() 方法(Python 2.7 中是 next());__iter__() 返回迭代器對象本身 self,__next__() 則返回每次調(diào)用 next() 或迭代時(shí)的元素; 內(nèi)置函數(shù) iter() 將可迭代對象轉(zhuǎn)化為迭代器
# iter(IterableObject) ita = iter([1, 2, 3]) print(type(ita)) print(next(ita)) print(next(ita)) print(next(ita)) # Create iterator Object class Container: def __init__(self, start = 0, end = 0): self.start = start self.end = end def __iter__(self): print('[LOG] I made this iterator!') return self def __next__(self): print('[LOG] Calling __next__ method!') if self.start i = self.start self.start += 1 return i else: raise StopIteration()
c = Container(0, 5) for i in c: print(i)
1 2 3 [LOG] I made this iterator! [LOG] Calling __next__ method! 0 [LOG] Calling __next__ method! 1 [LOG] Calling __next__ method! 2 [LOG] Calling __next__ method! 3 [LOG] Calling __next__ method! 4 [LOG] Calling __next__ method!
創(chuàng)建迭代器對象的好處是當(dāng)序列長度很大時(shí),可以減少內(nèi)存消耗,因?yàn)槊看沃恍枰涗浺粋€(gè)值即刻(經(jīng)常看到人們介紹 Python 2.7 的 range 函數(shù)時(shí),建議當(dāng)長度太大時(shí)用 xrange 更快,在 Python 3.5 中已經(jīng)去除了 xrange 只有一個(gè)類似迭代器一樣的 range)。
生成器
前面說到創(chuàng)建迭代器有3種方法,其中第三種就是生成器(generator)。生成器通過 yield 語句快速生成迭代器,省略了復(fù)雜的 __iter__() & __next__() 方式:
def container(start, end): while start yield start start += 1 c = container(0, 5) print(type(c)) print(next(c)) next(c)
for i in c: print(i)
0 2 3 4
簡單來說,yield 語句可以讓普通函數(shù)變成一個(gè)生成器,并且相應(yīng)的 __next__() 方法返回的是 yield 后面的值。一種更直觀的解釋是:程序執(zhí)行到 yield 會(huì)返回值并暫停,再次調(diào)用 next() 時(shí)會(huì)從上次暫停的地方繼續(xù)開始執(zhí)行:
def gen(): yield 5 yield 'Hello' yield 'World' yield 4 for i in gen(): print(i)
5 Hello World 4
Python 3.5 (準(zhǔn)確地說應(yīng)該是 3.3 以后)中為生成器添加了更多特性,包括 yield from 以及在暫停的地方傳值回生成器的 send()等,為了保持簡潔這里就不深入介紹了,有興趣可以閱讀官方文檔說明以及參考。
參考
|