python之美_Python之美[从菜鸟到高手]--生成器之全景分析
yield指令,可以暫停一個函數并返回中間結果。使用該指令的函數將保存執行環境,并且在必要時恢復。
生成器比迭代器更加強大也更加復雜,需要花點功夫好好理解貫通。
看下面一段代碼:
[python]
def gen():
for x in xrange(4):
tmp = yield x
if tmp == 'hello':
print 'world'
else:
print str(tmp)
def gen():
for x in xrange(4):
tmp = yield x
if tmp == 'hello':
print 'world'
else:
print str(tmp)
只要函數中包含yield關鍵字,該函數調用就是生成器對象。
[python]
g=gen()
print g?? #
print isinstance(g,types.GeneratorType) #True
g=gen()
print g?? #
print isinstance(g,types.GeneratorType) #True??? 我們可以看到,gen()并不是函數調用,而是產生生成器對象。
生成器對象支持幾個方法,如gen.next() ,gen.send() ,gen.throw()等。
[python]
print g.next() # 0
print g.next() # 0??? 調用生成器的next方法,將運行到yield位置,此時暫停執行環境,并返回yield后的值。所以打印出的是1,暫停執行環境。
[python]
print g.next() #None? 1
print g.next() #None? 1???? 再調用next方法,你也許會好奇,為啥打印出兩個值,不急,且聽我慢慢道來。
上一次調用next,執行到yield 0暫停,再次執行恢復環境,給tmp賦值(注意:這里的tmp的值并不是x的值,而是通過send方法接受的值),由于我們沒有調用send方法,所以
tmp的值為None,此時輸出None,并執行到下一次yield x,所以又輸出1.
到了這里,next方法我們都懂了,下面看看send方法。
[python]
print g.send('hello') #world? 2
print g.send('hello') #world? 2????? 上一次執行到yield 1后暫停,此時我們send('hello'),那么程序將收到‘hello',并給tmp賦值為’hello',此時tmp=='hello'為真,所以輸出'world',并執行到下一次yield 2,所以又打印出2.(next()等價于send(None))
當循環結束,將拋出StopIteration停止生成器。
看下面代碼:
[python]
def stop_immediately(name):
if name == 'skycrab':
yield 'okok'
else:
print 'nono'
s=stop_immediately('sky')
s.next()
def stop_immediately(name):
if name == 'skycrab':
yield 'okok'
else:
print 'nono'
s=stop_immediately('sky')
s.next() 正如你所預料的,打印出’nono',由于沒有額外的yield,所以將直接拋出StopIteration。
[python]
nono
Traceback (most recent call last):
File "F:python workspacePytestsrccs.py", line 170, in
s.next()
StopIteration
nono
Traceback (most recent call last):
File "F:python workspacePytestsrccs.py", line 170, in
s.next()
StopIteration
看下面代碼,理解throw方法,throw主要是向生成器發送異常。
[python]
def mygen():
try:
yield 'something'
except ValueError:
yield 'value error'
finally:
print 'clean'? #一定會被執行
gg=mygen()
print gg.next() #something
print gg.throw(ValueError) #value error? clean
def mygen():
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]
def tt():
for x in xrange(4):
print 'tt'+str(x)
yield
def gg():
for x in xrange(4):
print 'xx'+str(x)
yield
t=multitask.TaskManager()
t.add(tt())
t.add(gg())
t.run()
def tt():
for x in xrange(4):
print 'tt'+str(x)
yield
def gg():
for x in xrange(4):
print 'xx'+str(x)
yield
t=multitask.TaskManager()
t.add(tt())
t.add(gg())
t.run()
結果:
[python]
tt0
xx0
tt1
xx1
tt2
xx2
tt3
xx3
tt0
xx0
tt1
xx1
tt2
xx2
tt3
xx3?? 如果不是使用生成器,那么要實現上面現象,即函數交錯輸出,那么只能使用線程了,所以生成器給我們提供了更廣闊的前景。
如果僅僅是實現上面的效果,其實很簡單,我們可以自己寫一個。主要思路就是將生成器對象放入隊列,執行send(None)后,如果沒有拋出StopIteration,將該生成器對象再加入隊列。
[python]
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 xrange(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()
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 xrange(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實現的肯定不止這個功能,有興趣的童鞋可以看下源碼,還是比較簡單易懂的。
總結
以上是生活随笔為你收集整理的python之美_Python之美[从菜鸟到高手]--生成器之全景分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑鼠标间歇性卡顿怎么办 鼠标卡顿原因及
- 下一篇: python spark视频_Spark