Python:生成器
阿新 • • 發佈:2017-11-12
創建 進入 表達式 凍結 round 參數 () ffffff 保持
生成器算得上是Python語言中最吸引人的特性之一,生成器其實是一種特殊的叠代器,不過這種叠代器更加優雅。它不需要再像上面的類一樣寫 __iter__() 和 __next__() 方法了,只需要一個 yiled 關鍵字。
首先叠代提供的嵌套列表中的所有字列表,然後按順序叠代自列表中的元素。任何包含yield語句的函數稱為生成器。除了名字不同意外,它的行為和普通函數也有很大的差別,這就在於它不像return那樣返回值,而是每次產生多個值。每次產生一個值(使用yield語句),函數就會被凍結:即函數停在那點等待被激活,函數被激活後就從停止的那點開始執行
nested=[[1,2],[3,4],[5]]def flatten(nested): for sublist in nested: for element in sublist: yield element for num in flatten(nested): print(num) #1 # 2 # 3 # 4 # 5 print(list(flatten(nested)))#[1, 2, 3, 4, 5]
生成器兩種創建方式
1.(x*2 for x in range(3))
# a=[x*2 for x in range(1000000)]#不要試,死機 s=(x*2 for x inrange(3)) print(s)#<generator object <genexpr> at 0x00000203201E6938> print(next(s)) #等價於print(s.__next__()),in Py2: s.next() print(next(s))print(next(s)) print(next(s))#StopIteration # 生成器就是一個可叠代對象(iterable)
2.yield 生成器對象
生成器是一個包含yield關鍵字的函數。當它被調用時,在函數體中的代碼不會執行,而會返回一個叠代器。每次請求一個值,就會執行生成器中的代碼,知道遇到yield或者return語句。
yield語句意味著應該生成一個值,return語句意味著生成器要停止執行(不再生成任何東西,return語句只有在一個生成器中使用時才能進行無參數調用),換句話說生成器又兩部分組成:生成器的函數和生成器的叠代器生成器的函數使用def語句定義,包含yield,生成器的叠代器是這個函數返回的部分。
按一種不是很準確的說法,兩個實體經常被當做一個,合起來叫做生成器。生成器函數跟普通函數只有一點不一樣,就是把 return 換成yield,其中yield是一個語法糖,內部實現了叠代器協議,同時保持狀態可以掛起。
def foo(): print("Hello world") yield 1 print("ok") yield 2 foo()#生成器對象,不會執行代碼 g=foo() print(g)#<generator object foo at 0x00000230A33569E8> next(g) #Hello world next(g) #ok # next(g)#StopIteration for i in foo():#遍歷可叠代對象,對象擁有iter方法 print(i) #Hello world # 1 # ok # 2
用生成器來實現斐波那契數列的例子:一
def fib(max): n, a, b = 0, 0, 1 while n < max: # print(b) yield b a, b = b, a + b n = n + 1 return ‘done‘ f=fib(6) print(f)#<generator object fib at 0x0000025839E56990> #這裏,最難理解的就是generator和函數的執行流程不一樣。 # 函數是順序執行,遇到return語句或者最後一行函數語句就返回。 # 而變成generator的函數,在每次調用next()的時候執行,遇到yield語句返回, # 再次執行時從上次返回的yield語句處繼續執行。 print(f.__next__()) print(f.__next__()) print("________*****______") print(f.__next__()) print(f.__next__()) print(f.__next__())
結果:
1
1
________*****______
2
3
5
二
def fib(max): n, a, b = 0, 0, 1 while n < max: # print(b) yield b a, b = b, a + b n = n + 1 return ‘done‘ f=fib(6) while True: try: x=next(f) print(‘f:‘,x) except StopIteration as e: print("Generator return value:",e.value) break #結果 f: 1 f: 1 f: 2 f: 3 f: 5 f: 8 Generator return value: done
生成器新屬性是在開始運行後為生成器提供值的能力。表現為生成器和“外部世界”進行交流的渠道:
- 外部作用域訪問生成器的send方法,就像訪問next 方法一樣,只不過前者使用一個參數(發送的“消息”---任意對象)
- 在內部則掛起生成器,yield現在作為表達式而不是語句使用,換句話說,當生成器重新運行的時候,yield方法返回一個值,也就是外部通過send方法發送的值。如果next 方法被使用,那麽yield方法返回None.
- throw方法(使用異常類型調用,還有可選的值以及回溯對象)用於在生成器內引發一個異常(在yield表達式中)
- close 方法(調用時不用參數)用於停止生成器。
def bar():
print(‘ok1‘) count=yield 1 print(count) yield 2 b=bar() next(b) # s=b.send(None)#next(b) 第一次send前如果沒有next,只能傳一個send(None) # print(s) ret=b.send(‘eee‘) print(ret) # b.send(‘fff‘)
結果:
ok1
eee
2
send工作方法
def f(): print("ok") s=yield 7 print(s) yield 8 f=f() print(f.send(None)) # ok # 7 print(next(f)) # None # 8 #print(f.send(None))等同於print(next(f)),執行流程:打印ok,yield7,當再next進來時:將None賦值給s,然後返回8,可以通過斷點來觀察
吃包子案例
import time def consumer(name): print("%s 準備吃包子啦!" %name) while True: baozi = yield print("包子[%s]來了,被[%s]吃了!" %(baozi,name)) def producer(name): c = consumer(‘A‘)#生成器對象 c2 = consumer(‘B‘)#生成器對象 c.__next__()#執行生成器 c2.__next__() print("%s開始準備做包子啦!"%name) for i in range(1,6,2): time.sleep(1) print("做了2個包子!") c.send(i) c2.send(i+1) producer("greg")
協程應用:
所謂協同程序也就是是可以掛起,恢復,有多個進入點。其實說白了,也就是說多個函數可以同時進行,可以相互之間發送消息等。
import queue def tt(): for x in range(3): print (‘tt‘+str(x) ) yield def gg(): for x in range(3): print (‘xx‘+str(x) ) yield class Task(): def __init__(self): self._queue = queue.Queue() def add(self,gen): self._queue.put(gen) def run(self): while not self._queue.empty(): for i in range(self._queue.qsize()): try: gen= self._queue.get() gen.send(None) except StopIteration: pass else: self._queue.put(gen) t=Task() t.add(tt()) t.add(gg()) t.run() # tt0 # xx0 # tt1 # xx1 # tt2 # xx2
Python:生成器