生成器(generator)概念
生成器不會把結果保存在一個系列中,而是保存生成器的狀態,在每次進行迭代時返回一個值,直到遇到StopIteration異常結束。
生成器語法
生成器表達式: 通列表解析語法,只不過把列表解析的[]換成()
生成器表達式能做的事情列表解析基本都能處理,只不過在需要處理的序列比較大時,列表解析比較費記憶體。
>>> gen = (x**2 for x in range(5))
>>> gen
>> for gg in gen: print g, end='-')
...
0-1-4-9-16-
>>> for x in [0,1,2,3,4,5]:
... print( x, end='-')
...
0-1-2-3-4-5-
但是生成器函數可以生產一個無線的序列,這樣列表根本沒有辦法處理。
yield 的作用就是把一個函數變成一個 generator,帶有 yield 的函數不再是一個普通函數,Python 解釋器會將其視為一個 generator。
n=1
while True:
yield n
if count >=5: break
print(o )
count +=1
當然透過手動編寫迭代器可以實現類似的效果,只不過生成器更直觀易懂
class Iter:
def __init__(self):
def = __iter__(self):
return self def __next__(self):
self.start +=2
self.start +=2
區
print(next(I))
題外話: 生成器是包含有__iter()和next__()方法的,所以可以直接使用for來迭代,而沒有包含StopIteration的自編Iter來只能透過手動循環來迭代。
>>> from collections import Iterable
>>> from collections import Iterator
>>> isinstance(odd_num, Iterable)
>>> isodnce(odd_num, Iter)
True odd_numTrue
>>> help(odd_num)
Help on generator object:
odd = class generator(object)
| Methods defined here:
| | ter(self).
|
| __next__(self, /)
| Implement next(self).
......
看到上面的結果,現在你可以很有信心的按照Iterator的方式進行循環了吧!
在for 迴圈執行時,每次迴圈都會執行fab 函數內部的程式碼,當執行到yield b 時,fab 函數會傳回一個迭代值,下次迭代時,程式碼從yield b 的下一語句繼續執行,而函數的本地變數看起來和上次中斷執行前是完全一樣的,所以函數繼續執行,直到再次遇到yield。看起來好像一個函數在正常執行的過程中被 yield 中斷了數次,每次中斷都會透過 yield 傳回目前的迭代值。
yield 與return
在一個生成器中,如果沒有return,則預設執行到函數完成時返回StopIteration;
... yield 1
>> > g=g1()
>>> next(g) #第一次呼叫next(g)時,會在執行完yield語句後掛起,所以此時程式並沒有執行結束。1
>>> next(g) #程式試圖從yield語句的下一語句開始執行,發現已經到了結尾,所以拋出StopIteration異常。Traceback (most recent call last):
File "StopIteration
>>>>
如果遇到return,如果在執行過程中returnreturnation終止迭代。
>>> def g2():
... yield 'a'
... return
... yield 'b'
...
>>> g=g2()
>> next
) #程式停留在執行完yield 'a'語句後的位置。
'a'
>>> next(g) #程式發現下一語句是return,所以拋出StopIteration異常,這樣yield 'b'語句永遠也不會執行。
Traceback (most recent call last):
File "
StopIteration
>>> def g3():
... yield 'hello'
... return 'world'
...
>>> g=g3(ello)
>>> next(g)
>>> g=g3(ello)
>>> next(g)
')
>> '
>>> next(g)
File "
生成器支援的方法
> help(odd_num)
Help on generator object:
odd = class generator(object)
| Methods defined here:
...... 我 | generator.
|
| send(...)
| send(arg) -> send 'arg' into generator,
| return next yielded value
| return next yielded value 的 throw (typ[,val[,tb]]) -> raise exception in generator,
| return next yielded value or raise StopIteration.
......
後函數的呼叫會直接回傳StopIteration異常。
>>> def g4():
... yield 1... yield 2
... yield 3
...
>>> g=g4> 1
>>> g.close()
>>> next(g) #關閉後,yield 2和yield 3語句將不再起作用
Traceback (most recent call last):
File "
StopIteration
send()
生成器函數最大的特徵是可以接受外部傳入的一個變量,並根據變量內容計算結果後返回。
value=0
while True:
g=gen()
print(g.send(None))
print(g.send('aaa'))
print(g.send(3))
print(g.send('e'))
執行流程:
透過g.send(None)或next(g)可以啟動生成器函數,並執行到第一個yield語句結束的位置。
此時,執行完了yield語句,但是沒有給receive賦值。
yield value會輸出初始值0
注意:在啟動產生器函數時只能send(None),如果試圖輸入其它的值都會得到錯誤提示訊息。
此時yield value會輸出"got: aaa",然後掛起。
通過g.send(3),會重複第2步,最後輸出結果為"got: 3"
當我們g.send('e')時,程式會執行break然後推出循環,最後整個函數執行完畢,所以會得到StopIteration異常。
最後的執行結果如下:
got: aaa
got: 3
File "h.py", line 14, in
print' e'))
StopIteration
throw()
用來向生成器函數送入一個異常,可以結束系統定義的異常,或自訂的異常。
throw()後直接跑出異常並結束程序,或者消耗掉一個yield,或者在沒有下一個yield的時候直接進行到程序的結尾。
def gen():
while True:
try:
yield 'normal value'
yield 'normal value'
y print('here')
except ValueError:
print('we got Error here') :
break
g=gen()
print(next(g))
print(g.throw(ValueError))
print(next(g))
print(g.throw()
結果為:
we got ValueError here
normal valuenormal value 2
Traceback (most recent call last):
File "h.py", line 15, in
解釋:
print(next(g)):會輸出normal value,並停留在yield 'normal value 2'之前。
然後再次進入到while語句部分,消耗一個yield,所以會輸出normal value。
print(next(g)),會執行yield 'normal value 2'語句,並停留在執行完該語句後的位置。
下面給出一個綜合例子,用來把一個多維列表展開,或者說扁平化多維列表)
def flatten(nested):
try:
try:
if isinstance(nested, str):
raise TypeError
for sublist inbstedsted:a. for element in flatten(sublist):
#yield element類型Error:
#print('here')
yield nested
L=['aaadf',[1,2,35],2,48,48,8 '],7]]
for num in flatten(L):
print(num)
如果理解起來有點困難,那麼把print語句的註釋打開在進行查看就比較明了。
yield from
yield產生的函數就是一個迭代器,所以我們通常會把它放在循環語句中進行輸出結果。
例如下面的例子:
def inner():
for i in range(10): yield i
def outer():
yield i
res = g_inner.send(None)
yield res
g_outer=outer()
while True:
try:
Stop. break
此時,我們可以採用yield from語句來減少我麼你的工作量。
def outer2():
yield from inner()
http://blog.theerrorlog.com/yield-from-in-python-3.html
http://stackoverflow.com/questions/9708902/in-practice-what-are-the-main-uses-for -the-new-yield-from-syntax-in-python-3
總結
第一次執行next(generator)時,會執行完yield語句後程式進行掛起,所有的參數和狀態會進行保存。
在遇到程式的結尾或遇到StopIteration時,循環結束。
可以透過generator.send(arg)來傳入參數,這是協程模型。
可以透過generator.throw(exception)來傳入一個異常。 throw語句會消耗掉一個yield。
可以透過generator.close()來手動關閉生成器。
next()等價於send(None)