生成器的概念
生成器不會把結果保存在一個系列中,而是保存生成器的狀態(tài),在每次進行迭代時返回一個值膊毁,直到遇到StopIteration異常結束凸克。
注: 本文嚴重參考文章 深入理解Python中的生成器 僅作為個人練習&理解&備忘用。
- 列表與生成器區(qū)別
#普通列表
gen = [x*2 for x in range(5)]
print('普通列表 := %s ' % gen)
#生成器
gen = (x*2 for x in range(5))
print('生成器 := %s' % gen)
普通列表 := [0, 2, 4, 6, 8]
生成器 := <generator object <genexpr> at 0x000000000505A200>
- 生成器實例1
#例子1
def odd(max_n):
n=1
while n <= max_n:
yield n
n+=2
for o in odd(5):
print(o)
1
3
5
- 生成器實例2
#著名的斐波拉契數(shù)列
def fib(max_n):
a,b = 1,2
while b <= max_n :
yield a
a,b = b,a+b
for o in fib(100):
print(o)
1
2
3
5
8
13
21
34
55
- 生成器實例3 (next調用 )
#實踐next方法
def odd(max_n):
n=1
while n <= max_n:
yield n
n+=2
func = odd(5)
print(func.__next__())
print(func.__next__())
print(func.__next__())
print(func.__next__())
1
3
5
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-16-69e5c7ba2b2c> in <module>()
10 print(func.__next__())
11 print(func.__next__())
---> 12 print(func.__next__())
StopIteration:
- yield 與 return
在一個生成器中蒲障,如果沒有return歹篓,則默認執(zhí)行到函數(shù)完畢時返回StopIteration;
如果遇到return,如果在執(zhí)行過程中 return揉阎,則直接拋出 StopIteration 終止迭代庄撮。
#帶有return的生成器方法
def gen():
yield 1
yield 2
return
yield 3
for o in gen():
print(o)
print('=====漂亮的分隔符=====')
test = gen()
print(test.__next__())
print(test.__next__())
print(test.__next__())
1
2
=====漂亮的分隔符=====
1
2
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-21-804ab8c50d0c> in <module>()
13 print(test.__next__())
14 print(test.__next__())
---> 15 print(test.__next__())
16
17
StopIteration:
- 生成器支持的方法
help(odd(5))
Help on generator object:
odd = class generator(object)
| Methods defined here:
|
| __del__(...)
|
| __getattribute__(self, name, /)
| Return getattr(self, name).
|
| __iter__(self, /)
| Implement iter(self).
|
| __next__(self, /)
| Implement next(self).
|
| __repr__(self, /)
| Return repr(self).
|
| close(...)
| close() -> raise GeneratorExit inside generator.
|
| send(...)
| send(arg) -> send 'arg' into generator,
| return next yielded value or raise StopIteration.
|
| throw(...)
| throw(typ[,val[,tb]]) -> raise exception in generator,
| return next yielded value or raise StopIteration.
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| gi_code
|
| gi_frame
|
| gi_running
|
| gi_yieldfrom
| object being iterated by yield from, or None
- close方法
手動關閉生成器函數(shù),后面的調用會直接返回StopIteration異常毙籽。
# close() 手動關閉生成器函數(shù)洞斯,后面的調用會直接返回StopIteration異常。
def gen():
yield 1
yield 2
yield 3
g=gen()
print(g.__next__())
g.close()
print(g.__next__())
1
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-27-3e8b7df380d6> in <module>()
8 print(g.__next__())
9 g.close()
---> 10 print(g.__next__())
11
12
StopIteration:
- send 方法
生成器函數(shù)最大的特點是可以接受外部傳入的一個變量坑赡,并根據(jù)變量內容計算結果后返回烙如。
# send()
# 生成器函數(shù)最大的特點是可以接受外部傳入的一個變量,并根據(jù)變量內容計算結果后返回毅否。
def gen():
value='atp'
while True:
receive=yield value
if receive=='end':
break
value = '%s' % receive
g=gen()
print(g.send(None))
print(g.send('abc'))
print(g.send(3))
print(g.send('end'))
atp
abc
3
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-30-e5080153cccf> in <module>()
14 print(g.send('abc'))
15 print(g.send(3))
---> 16 print(g.send('end'))
StopIteration:
執(zhí)行流程:
- 通過g.send(None)或者next(g)可以啟動生成器函數(shù)亚铁,并執(zhí)行到第一個yield語句結束的位置。此時螟加,執(zhí)行完了yield語句徘溢,但是沒有給receive賦值。yield value會輸出初始值0注意:在啟動生成器函數(shù)時只能send(None),如果試圖輸入其它的值都會得到錯誤提示信息捆探。
- 通過g.send(‘abc’)然爆,會傳入abc,并賦值給receive黍图,然后計算出value的值曾雕,并回到while頭部,執(zhí)行yield value語句有停止雌隅。此時yield value會輸出”abc”翻默,然后掛起。
- 通過g.send(3)恰起,會重復第2步修械,最后輸出結果為”3″
- 當我們g.send(‘e’)時,程序會執(zhí)行break然后推出循環(huán)检盼,最后整個函數(shù)執(zhí)行完畢肯污,所以會得到StopIteration異常。
- throw方法
用來向生成器函數(shù)送入一個異常,可以結束系統(tǒng)定義的異常蹦渣,或者自定義的異常哄芜。throw()后直接跑出異常并結束程序.
# throw()
# 用來向生成器函數(shù)送入一個異常,可以結束系統(tǒng)定義的異常柬唯,或者自定義的異常认臊。
# throw()后直接跑出異常并結束程序.
def gen():
while True:
try:
yield 'normal value'
yield 'normal value 2'
print('here')
yield 'normal value 3'
except ValueError:
print('we got ValueError here')
except TypeError:
print('we got TypeError here')
break
g=gen()
print(next(g))
print(g.throw(ValueError))
print(next(g))
print(g.throw(TypeError))
normal value
we got ValueError here
normal value
normal value 2
we got TypeError here
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-38-fbedd6d510de> in <module>()
21 print(g.throw(ValueError))
22 print(next(g))
---> 23 print(g.throw(TypeError))
StopIteration:
解釋:
- print(next(g)):會輸出normal value,并停留在yield ‘normal value 2’之前锄奢。
由于執(zhí)行了g.throw(ValueError)失晴,所以會跳過所有后續(xù)的try語句,也就是說yield ‘normal value 2’不會被執(zhí)行拘央,然后進入到except語句涂屁,打印出we got ValueError here。然后再次進入到while語句部分灰伟,消耗一個yield拆又,所以會輸出normal value。- print(next(g))栏账,會執(zhí)行yield ‘normal value 2’語句帖族,并停留在執(zhí)行完該語句后的位置。g.throw(TypeError):會跳出try語句发笔,從而print(‘here’)不會被執(zhí)行盟萨,然后執(zhí)行break語句凉翻,跳出while循環(huán)了讨,然后到達程序結尾,所以跑出StopIteration異常制轰。
def gen():
try:
yield 'normal value'
yield 'normal value 2'
print('here')
yield 'normal value 3'
except ValueError:
print('we got ValueError here')
except TypeError:
pass
g=gen()
print(next(g))
print(g.throw(ValueError))
print(next(g))
normal value
we got ValueError here
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-39-b846253db189> in <module>()
13 g=gen()
14 print(next(g))
---> 15 print(g.throw(ValueError))
16 print(next(g))
StopIteration:
總結
- 按照鴨子模型理論前计,生成器就是一種迭代器,可以使用for進行迭代垃杖。
- 第一次執(zhí)行next(generator)時男杈,會執(zhí)行完yield語句后程序進行掛起盐股,所有的參數(shù)和狀態(tài)會進行保存拷呆。再一次執(zhí)行next(generator)時,會從掛起的狀態(tài)開始往后執(zhí)行项戴。在遇到程序的結尾或者遇到StopIteration時彩库,循環(huán)結束肤无。
- 可以通過generator.send(arg)來傳入參數(shù),這是協(xié)程模型骇钦。
- 可以通過generator.throw(exception)來傳入一個異常宛渐。throw語句會消耗掉一個yield。
- 可以通過generator.close()來手動關閉生成器。
- next()等價于send(None)
試驗1:素數(shù)生成器
#奇數(shù)生成方法
def _odd_iter(n):
while True:
yield n
n = n + 2
#過濾掉隊列中能被n整除的元素
def _not_divisible(n):
return lambda x: x % n > 0
#素數(shù)生成器
def primes():
yield 2
it = _odd_iter() # 初始序列
while True:
n = next(it) # 返回序列的第一個數(shù)
yield n
it = filter(_not_divisible(n), it) # 構造新序列
for n in primes():
if n < 1000:
print(n)
else:
break