生成器
1,生成器定義
一邊循環(huán)一邊計(jì)算的機(jī)制,稱為生成器,generator。generator保存的是算法。
2,生成器的創(chuàng)建(法一)
(1)把一個(gè)列表生成式的[]改成()
>>> 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 0x1022ef630>
(2)打印generator的元素:next()和for循環(huán)
>>> 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
>>> g = (x * x for x in range(10))
>>> for n in g:
... print(n)
...
0
1
4
9
16
25
36
49
64
81
一般不會(huì)調(diào)用next(),而是通過for循環(huán)來迭代它,并且不需要關(guān)心StopIteration的錯(cuò)誤。
3,生成器的創(chuàng)建(方法二)
如果推算的算法比較復(fù)雜,用類似列表生成式的for循環(huán)無法實(shí)現(xiàn)的時(shí)候,還可以用函數(shù)來實(shí)現(xiàn)。
(1)斐波拉契數(shù)列(Fibonacci)的fib函數(shù)
1, 1, 2, 3, 5, 8, 13, 21, 34, ...:除第一個(gè)和第二個(gè)數(shù)外,任意一個(gè)數(shù)都可由前兩個(gè)數(shù)相加得到:
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b)
a, b = b, a + b
n = n + 1
return 'done'
(2)斐波拉契數(shù)列(Fibonacci)的generator函數(shù)
只需要把fib函數(shù)中的print(b)改為yield b就可以
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
- 生成器定義:如果一個(gè)函數(shù)定義中包含
yield關(guān)鍵字,那么這個(gè)函數(shù)就不再是一個(gè)普通函數(shù),而是一個(gè)generator函數(shù),調(diào)用一個(gè)generator函數(shù)將返回一個(gè)generator -
generator函數(shù)不等于generator
>>> f = fib(6)
>>> f
<generator object fib at 0x104feaaa0>
(3)注意:調(diào)用generator函數(shù)會(huì)創(chuàng)建一個(gè)generator對象,多次調(diào)用generator函數(shù)會(huì)創(chuàng)建多個(gè)相互獨(dú)立的generator
例:定義一個(gè)generator函數(shù),依次返回?cái)?shù)字1,3,5:
def odd():
print('step 1')
yield 1
print('step 2')
yield(3)
print('step 3')
yield(5)
調(diào)用該generator函數(shù)時(shí),首先要生成一個(gè)generator對象,然后用next()函數(shù)不斷獲得下一個(gè)返回值:
>>> o = odd()
>>> next(o)
step 1
1
>>> next(o)
step 2
3
>>> next(o)
step 3
5
>>> next(o)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
odd不是普通函數(shù),而是generator函數(shù),在執(zhí)行過程中,遇到yield就中斷,下次又繼續(xù)執(zhí)行。執(zhí)行3次yield后,已經(jīng)沒有yield可以執(zhí)行了,所以,第4次調(diào)用next(o)就報(bào)錯(cuò)。
然而,
>>> next(odd())
step 1
1
>>> next(odd())
step 1
1
>>> next(odd())
step 1
1
原因在于odd()會(huì)創(chuàng)建一個(gè)新的generator對象,上述代碼實(shí)際上創(chuàng)建了3個(gè)完全獨(dú)立的generator,對3個(gè)generator分別調(diào)用next()當(dāng)然每個(gè)都會(huì)返回第一個(gè)值。
4,練習(xí)
楊輝三角定義如下:

把每一行看做一個(gè)list,試寫一個(gè)generator,不斷輸出下一行的list:
# -*- coding: utf-8 -*-
def triangles():
L = [1]
while True:
yield L
L = [1] + [L[n] + L[n + 1] for n in range(len(L) - 1)] + [1]
#測試生成器輸出
# 期待輸出:
# [1]
# [1, 1]
# [1, 2, 1]
# [1, 3, 3, 1]
# [1, 4, 6, 4, 1]
# [1, 5, 10, 10, 5, 1]
# [1, 6, 15, 20, 15, 6, 1]
# [1, 7, 21, 35, 35, 21, 7, 1]
# [1, 8, 28, 56, 70, 56, 28, 8, 1]
# [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
n = 0
results = []
for t in triangles():
results.append(t)
n = n + 1
if n == 10:
break
for t in results:
print(t)
if results == [
[1],
[1, 1],
[1, 2, 1],
[1, 3, 3, 1],
[1, 4, 6, 4, 1],
[1, 5, 10, 10, 5, 1],
[1, 6, 15, 20, 15, 6, 1],
[1, 7, 21, 35, 35, 21, 7, 1],
[1, 8, 28, 56, 70, 56, 28, 8, 1],
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
]:
print('測試通過!')
else:
print('測試失敗!')
Run
迭代器
1,Iterable和Iterator
(1)可以直接作用于for循環(huán)的對象統(tǒng)稱為可迭代對象:Iterable。
可以被next()函數(shù)調(diào)用并不斷返回下一個(gè)值的對象稱為迭代器:Iterator。
(2)可以使用isinstance()判斷一個(gè)對象是否是Iterable對象:
>>> from collections.abc import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False
也可以使用isinstance()判斷一個(gè)對象是否是Iterator對象:
>>> from collections.abc import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False
2,Iterable和Iterator轉(zhuǎn)換
生成器都是Iterator對象,但list、dict、str雖然是Iterable,卻不是Iterator。
把list、dict、str等Iterable變成Iterator可以使用iter()函數(shù):
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True
3,為什么list、dict、str等數(shù)據(jù)類型不是Iterator?
答:這是因?yàn)镻ython的Iterator對象表示的是一個(gè)數(shù)據(jù)流,Iterator對象可以被next()函數(shù)調(diào)用并不斷返回下一個(gè)數(shù)據(jù),直到?jīng)]有數(shù)據(jù)時(shí)拋出StopIteration錯(cuò)誤??梢园堰@個(gè)數(shù)據(jù)流看做是一個(gè)有序序列,但我們卻不能提前知道序列的長度,只能不斷通過next()函數(shù)實(shí)現(xiàn)按需計(jì)算下一個(gè)數(shù)據(jù),所以Iterator的計(jì)算是惰性的,只有在需要返回下一個(gè)數(shù)據(jù)時(shí)它才會(huì)計(jì)算。
Iterator甚至可以表示一個(gè)無限大的數(shù)據(jù)流,例如全體自然數(shù)。而使用list是永遠(yuǎn)不可能存儲全體自然數(shù)的。
4,小結(jié)
凡是可作用于for循環(huán)的對象都是Iterable類型;
凡是可作用于next()函數(shù)的對象都是Iterator類型,它們表示一個(gè)惰性計(jì)算的序列;
集合數(shù)據(jù)類型如list、dict、str等是Iterable但不是Iterator,不過可以通過iter()函數(shù)獲得一個(gè)Iterator對象。
Python的for循環(huán)本質(zhì)上就是通過不斷調(diào)用next()函數(shù)實(shí)現(xiàn)的,例如:
for x in [1, 2, 3, 4, 5]:
pass
實(shí)際上完全等價(jià)于:
# 首先獲得Iterator對象:
it = iter([1, 2, 3, 4, 5])
# 循環(huán):
while True:
try:
# 獲得下一個(gè)值:
x = next(it)
except StopIteration:
# 遇到StopIteration就退出循環(huán)
break