生成器之全景分析

2021-07-14 22:49:58 字數 4851 閱讀 6498

yield指令,可以暫停乙個函式並返回中間結果。使用該指令的函式將儲存執行環境,並且在必要時恢復。

生成器比迭代器更加強大也更加複雜,需要花點功夫好好理解貫通。

看下面一段**:

[python]view plain

copy

defgen():  

forx 

inxrange(

4):  

tmp = yield

x  if

tmp == 

'hello'

:  print

'world'

else

:  print

str(tmp)  

只要函式中包含yield關鍵字,該函式呼叫就是生成器物件。

[python]view plain

copy

g=gen()  

print

g   

#print

isinstance(g,types.generatortype) 

#true

我們可以看到,gen()並不是函式呼叫,而是產生生成器物件。

生成器物件支援幾個方法,如gen.next() ,gen.send() ,gen.throw()等。

[python]view plain

copy

print

g.next() 

# 0

呼叫生成器的next方法,將執行到yield位置,此時暫停執行環境,並返回yield後的值。所以列印出的是0,暫停執行環境。

[python]view plain

copy

print

g.next() 

#none  1

再呼叫next方法,你也許會好奇,為啥列印出兩個值,不急,且聽我慢慢道來。

上一次呼叫next,執行到yield 0暫停,再次執行恢復環境,給tmp賦值(注意:這裡的tmp的值並不是x的值,而是通過send方法接受的值),由於我們沒有呼叫send方法,所以

tmp的值為none,此時輸出none,並執行到下一次yield x,所以又輸出1.

到了這裡,next方法我們都懂了,下面看看send方法。

[python]view plain

copy

print

g.send(

'hello'

) #world  2

上一次執行到yield 1後暫停,此時我們send('hello'),那麼程式將收到『hello',並給tmp賦值為』hello',此時tmp=='hello'為真,所以輸出'world',並執行到下一次yield 2,所以又列印出2.(next()等價於send(none))

當迴圈結束,將丟擲stopiteration停止生成器。

看下面**:

[python]view plain

copy

defstop_immediately(name):  

ifname == 

'skycrab'

:  yield

'okok'

else

:  print

'nono'

s=stop_immediately('sky'

)  s.next()  

正如你所預料的,列印出』nono',由於沒有額外的yield,所以將直接丟擲stopiteration。

[python]view plain

copy

nono  

traceback (most recent call last):  

file "f:\python workspace\pytest\src\cs.py"

, line 

170, 

ins.next()  

stopiteration  

看下面**,理解throw方法,throw主要是向生成器傳送異常。

[python]view plain

copy

defmygen():  

try:  

yield

'something'

except

valueerror:  

yield

'value error'

finally

:  print

'clean'

#一定會被執行

gg=mygen()  

print

gg.next() 

#something

print

gg.throw(valueerror) 

#value error  clean

呼叫gg.next很明顯此時輸出『something』,並在yield 『something』暫停,此時向gg傳送valueerror異常,恢復執行環境,except  將會捕捉,並輸出資訊。

理解了這些,我們就可以向協同程式發起攻擊了,所謂協同程式也就是是可以掛起,恢復,有多個進入點。其實說白了,也就是說多個函式可以同時進行,可以相互之間傳送訊息等。

這裡有必要說一下multitask模組(不是標準庫中的),看一段multitask使用的簡單**:

[python]view plain

copy

deftt():  

forx 

inxrange(

4):  

print

'tt'

+str(x)  

yield

defgg():  

forx 

inxrange(

4):  

print

'xx'

+str(x)  

yield

t=multitask.taskmanager()  

t.add(tt())  

t.add(gg())  

t.run()  

結果:[python]view plain

copy

tt0  

xx0  

tt1  

xx1  

tt2  

xx2  

tt3  

xx3  

如果不是使用生成器,那麼要實現上面現象,即函式交錯輸出,那麼只能使用執行緒了,所以生成器給我們提供了更廣闊的前景。 

如果僅僅是實現上面的效果,其實很簡單,我們可以自己寫乙個。主要思路就是將生成器物件放入佇列,執行send(none)後,如果沒有丟擲stopiteration,將該生成器物件再加入佇列。

[python]view plain

copy

class

task():  

def__init__(

self

):  

self

._queue = queue.queue()  

defadd(

self

,gen):  

self

._queue.put(gen)  

defrun(

self

):  

while

notself

._queue.empty():  

fori 

inxrange(

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()  

當然,multitask實現的肯定不止這個功能,有興趣的童鞋可以看下原始碼,還是比較簡單易懂的。

#增補 2014/5/21

之前我在南京面試python時遇到這麼一道題目:

[python]view plain

copy

defthread1():  

forx 

inrange(

4):  

yield

x  def

thread2():  

forx 

inrange(4,

8):  

yield

x  threads=  

defrun(threads): 

#寫這個函式,模擬線程併發

pass

run(threads)  

如果上面class task看懂了,那麼這題很簡單,其實就是考你用yield模擬線程排程,解決如下:

[python]view plain

copy

defrun(threads):  

fort 

inthreads:  

try:  

print

t.next()  

except

stopiteration:  

pass

else

:  

生成器原理分析

生成器是一種快速完成迭代器功能的工具,是一種特殊的迭代器。通過在函式中,設定關鍵字yield,即為生成器函式。def student yield 1 yield 2 yield 3 為什麼說生成器是一種特殊的迭代器,可以通過isinstance函式判斷。可以看到,生成器是可迭代物件,也是迭代器物件。...

分析報告生成器,Word文件自動生成器

多特 軟體介紹 該軟體為共享軟體,如果你喜歡這個軟體,並且能為你帶來價值,請購買。軟體 為 2900元 套.乙個使用者可以部署在兩台電腦上執行。在日常工作中,你一定遇到這樣的事情,經常要在每個固定時間出乙個報告,或簡單或複雜。每次還可能要改動一些引數。報告的形式可能是文字描述,也有圖表,但格式都不固...

python之生成器

使用生成器表示式取代列表解析可以同時節省cpu 和 記憶體 ram 如果你構造乙個列表的目的僅僅是傳遞給別的函式,比如 傳遞給tuple 或者set 用生成器表示式替代吧 def ord map a string for c in a string yield ord c gen ord map u...