項(xiàng)目地址:https://git.io/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 < self.end:
i = self.start
self.start += 1
return i
else:
raise StopIteration()
c = Container(0, 5)
for i in c:
print(i)
<class 'list_iterator'>
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 < end:
yield start
start += 1
c = container(0, 5)
print(type(c))
print(next(c))
next(c)
for i in c:
print(i)
<class 'generator'>
0
2
3
4
簡單來說孽江,yield
語句可以讓普通函數(shù)變成一個(gè)生成器讶坯,并且相應(yīng)的 __next__()
方法返回的是 yield
后面的值。一種更直觀的解釋是:程序執(zhí)行到 yield
會返回值并暫停岗屏,再次調(diào)用 next()
時(shí)會從上次暫停的地方繼續(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()
等,為了保持簡潔這里就不深入介紹了这刷,有興趣可以閱讀官方文檔說明以及參考鏈接2婉烟。