>>squares=[n*nforninrange(3)]>>>foriinsquares:printi014這種創建列表的操作很常見,稱為列表推導。但是像列表這樣的迭代器,比如str、file等,雖然用起來很方便,但有一點,它們是儲存在內存中的,如果值很大,會很麻煩。而生成器表達式不同,它執行的計算與列表包含相同,但會迭代的生成結果。它的語法與列表推導一樣," />

欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

Python中的生成器和yield詳細介紹

系統 1608 0

列表推導與生成器表達式

當我們創建了一個列表的時候,就創建了一個可以迭代的對象:

復制代碼 代碼如下:

>>> squares=[n*n for n in range(3)]
>>> for i in squares:
?print i
?
0
1
4

這種創建列表的操作很常見,稱為列表推導。但是像列表這樣的迭代器,比如str、file等,雖然用起來很方便,但有一點,它們是儲存在內存中的,如果值很大,會很麻煩。

而生成器表達式不同,它執行的計算與列表包含相同,但會迭代的生成結果。它的語法與列表推導一樣,只是要用小括號來代替中括號:

復制代碼 代碼如下:

>>> squares=(n*n for n in range(3))
>>> for i in squares:
?print i
?
0
1
4

生成器表達式不會創建序列形式的對象,不會把所有的值都讀取到內存中,而是會創建一個通過迭代并按照需求生成值的生成器對象(Generator)。

那么,還有沒有其它方法來產生生成器呢?

例子:斐波那契數列

例如有個需求,要生成斐波那契數列的前10位,我們可以這樣寫:

復制代碼 代碼如下:

def fib(n):
??? result=[]
??? a=1
??? b=1
??? result.append(a)
??? for i in range(n-1):
??????? a,b=b,a+b
??????? result.append(a)
??? return result
if __name__=='__main__':
??? print fib(10)

數字很少時,函數運行良好,但數字很多時,問題就來了,顯然生成一個幾千幾萬長度的列表并不是一個很好的主意。

這樣,需求就變成了:寫一個可以生成可迭代對象的函數,或者說,不要讓函數一次返回全部的值,而是一次返回一個值。

這好像與我們的常識相違背,當我們調用一個普通的Python函數時,一般是從函數的第一行代碼開始執行,結束于return語句、異常或者函數結束(可以看作隱式的返回None):

復制代碼 代碼如下:

def fib(n):
??? a=1
??? b=1
??? for i in range(n-1):
??????? a,b=b,a+b
??????? return a
if __name__=='__main__':
??? print fib(10)
>>>
1??? #返回第一個值時就卡住了

函數一旦將控制權交還給調用者,就意味著全部結束。函數中做的所有工作以及保存在局部變量中的數據都將丟失。再次調用這個函數時,一切都將從頭創建。函數只有一次返回結果的機會,因而必須一次返回所有的結果。通常我們都這么認為的。但是,如果它們并非如此呢?請看神奇的yield:
復制代碼 代碼如下:

def fib(n):
??? a=1
??? yield a
??? b=1
??? for i in range(n-1):
??????? a,b=b,a+b
??????? yield a
if __name__=='__main__':
??? for i in fib(10):
??????? print i
>>>
1
1
2
3
5
8
13
21
34

生成器Generator

python中生成器的定義很簡單,使用了yield關鍵字的函數就可以稱之為生成器,它生成一個值的序列:

復制代碼 代碼如下:

def countdown(n):
??? while n>0:
??????? yield n
??????? n-=1
if __name__=='__main__':
??? for i in countdown(10):
??????? print i

生成器函數返回生成器。要注意的是生成器就是一類特殊的迭代器。作為一個迭代器,生成器必須要定義一些方法,其中一個就是__next__()。如同迭代器一樣,我們可以使用next()函數(Python3是__next__() )來獲取下一個值:
復制代碼 代碼如下:

>>> c=countdown(10)
>>> c.next()
10
>>> c.next()
9

每當生成器被調用的時候,它會返回一個值給調用者。在生成器內部使用yield來完成這個動作。為了記住yield到底干了什么,最簡單的方法是把它當作專門給生成器函數用的特殊的return。調用next()時,生成器函數不斷的執行語句,直至遇到yield為止,此時生成器函數的”狀態”會被凍結,所有的變量的值會被保留下來,下一行要執行的代碼的位置也會被記錄,直到再次調用next()繼續執行yield之后的語句。

next()不能無限執行,當迭代結束時,會拋出StopIteration異常。迭代未結束時,如果你想結束生成器,可以使用close()方法。

復制代碼 代碼如下:

>>> c.next()
1
>>> c.next()
StopIteration
>>> c=countdown(10)
>>> c.next()
10
>>> c.close()
>>> c.next()
StopIteration

協程與yield表達式

yield語句還有更給力的功能,作為一個語句出現在賦值運算符的右邊,接受一個值,或同時生成一個值并接受一個值。

復制代碼 代碼如下:

def recv():
??? print 'Ready'
??? while True:
??????? n=yield
??????? print 'Go %s'%n
>>> c=recv()
>>> c.next()
Ready
>>> c.send(1)
Go 1
>>> c.send(2)
Go 2

以這種方式使用yield語句的函數稱為協程。在這個例子中,對于next()的初始調用是必不可少的,這樣協程才能執行可通向第一個yield表達式的語句。在這里協程會掛起,等待相關生成器對象send()方法給它發送一個值。傳遞給send()的值由協程中的yield表達式返回。

協程的運行一般是無限期的,使用方法close()可以顯式的關閉它。

如果yield表達式中提供了值,協程可以使用yield語句同時接收和發出返回值。

復制代碼 代碼如下:

def split_line():
??? print 'ready to split'
??? result=None
??? while True:
??????? line=yield result
??????? result=line.split()
>>> s=split_line()
>>> s.next()
ready to split
>>> s.send('1 2 3')
['1', '2', '3']
>>> s.send('a b c')
['a', 'b', 'c']

注意:理解這個例子中的先后順序非常重要。首個next()方法讓協程執行到yield result,這將返回result的值None。在接下來的send()調用中,接收到的值被放到line中并拆分到result中。send()方法的返回值就是下一條yield語句的值。也就是說,send()方法可以將一個值傳遞給yield表達式,但是其返回值來自下一個yield表達式,而不是接收send()傳遞的值的yield表達式。

如果你想用send()方法來開啟協程的執行,必須先send一個None值,因為這時候是沒有yield語句來接受值的,否則就會拋出異常。

復制代碼 代碼如下:

>>> s=split_line()
>>> s.send('1 2 3')
TypeError: can't send non-None value to a just-started generator
>>> s=split_line()
>>> s.send(None)
ready to split

使用生成器與協程

乍看之下,如何使用生成器和協程解決實際問題似乎并不明顯。但在解決系統、網絡和分布式計算方面的某些問題時,生成器和協程特別有用。實際上,yield已經成為Python最強大的關鍵字之一。

比如,要建立一個處理文件的管道:

復制代碼 代碼如下:

import os,sys
def default_next(func):
??? def start(*args,**kwargs):
??????? f=func(*args,**kwargs)
??????? f.next()
??????? return f
??? return start
@default_next
def find_files(target):
??? topdir=yield
??? while True:
??????? for path,dirname,filelist in os.walk(topdir):
??????????? for filename in filelist:
??????????????? target.send(os.path.join(path,filename))

@default_next
def opener(target):
??? while True:
??????? name=yield
??????? f=open(name)
??????? target.send(f)
???
@default_next
def catch(target):
??? while True:
??????? f=yield
??????? for line in f:
??????????? target.send(line)
???????????
@default_next
def printer():
??? while True:
??????? line=yield
??????? print line


然后將這些協程連接起來,就可以創建一個數據流處理管道了:
復制代碼 代碼如下:

finder=find_files(opener(catch(printer())))
finder.send(toppath)

程序的執行完全由將數據發送到第一個協程find_files()中來驅動,協程管道會永遠保持活動狀態,直到它顯式的調用close()。

總之,生成器的功能非常強大。協程可以用于實現某種形式的并發。在某些類型的應用程序中,可以用一個任務調度器和一些生成器或協程實現協作式用戶空間多線程,即greenlet。yield的威力將在協程,協同式多任務處理(cooperative multitasking),以及異步IO中得到真正的體現。


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 中文字幕亚洲欧美 | 99久久精品国产片 | 日韩欧美一区二区三区 | 成年人激情在线 | 天天操网| 狠狠干成人 | 日本大人吃奶视频xxxx | 欧美亚洲国产日韩 | 青娱分类视频精品免费2 | 欧美午夜伦理片 | 91福利精品老师国产自产在线 | 亚洲1区2区3区4区 | 人人澡人人澡人人看添欧美 | 亚洲精品性视频 | a级成人毛片久久 | 天天摸日日操 | 哥斯拉大战金刚2在线观看免费完整版 | 漂流教室在线观看 | 国产99精品一区二区三区免费 | 无码色情影片视频在线看免费 | 久久久久亚洲一区二区三区 | 欧美日韩精品国产一区二区 | 欧美三级在线 | 日本中文字幕一区二区有码在线 | 激情五月综合 | 日韩国产欧美在线观看一区二区 | 精品在线不卡 | 九九九精品视频免费 | 一区二区三区在线播放 | 日韩一区不卡 | 欧美精品成人一区二区三区四区 | 午夜免费观看福利片一区二区三区 | 日日狠日 | 国产一二三区精品 | 亚洲午夜在线 | 久久久久国产成人精品亚洲午夜 | 亚洲精品乱码久久久久久蜜桃 | 欧美精品国产一区二区三区 | 欧美啊啊啊 | 亚洲码欧美码一区二区三区 | 亚洲日本乱码在线观看 |