yield指令,可以暫停乙個函式並返回中間結果。使用該指令的函式將儲存執行環境,並且在必要時恢復。
生成器比迭代器更加強大也更加複雜,需要花點功夫好好理解貫通。
看下面一段**:
[python]view plain
copy
defgen():
forx
inxrange(
4):
tmp = yield
x if
tmp ==
'hello'
'world'
else
str(tmp)
只要函式中包含yield關鍵字,該函式呼叫就是生成器物件。
[python]view plain
copy
g=gen()
g
isinstance(g,types.generatortype)
#true
我們可以看到,gen()並不是函式呼叫,而是產生生成器物件。
生成器物件支援幾個方法,如gen.next() ,gen.send() ,gen.throw()等。
[python]view plain
copy
g.next()
# 0
呼叫生成器的next方法,將執行到yield位置,此時暫停執行環境,並返回yield後的值。所以列印出的是0,暫停執行環境。
[python]view plain
copy
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
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
'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
'clean'
#一定會被執行
gg=mygen()
gg.next()
#something
gg.throw(valueerror)
#value error clean
呼叫gg.next很明顯此時輸出『something』,並在yield 『something』暫停,此時向gg傳送valueerror異常,恢復執行環境,except 將會捕捉,並輸出資訊。
理解了這些,我們就可以向協同程式發起攻擊了,所謂協同程式也就是是可以掛起,恢復,有多個進入點。其實說白了,也就是說多個函式可以同時進行,可以相互之間傳送訊息等。
這裡有必要說一下multitask模組(不是標準庫中的),看一段multitask使用的簡單**:
[python]view plain
copy
deftt():
forx
inxrange(
4):
'tt'
+str(x)
yield
defgg():
forx
inxrange(
4):
'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:
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...