生成器表達式python-ag真人国际官网
1. python中列表生成式和生成器的區別
列表生成式語法:
[x*x for x in range(0,10)] //列表生成式,這里是中括弧
//結果 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
(x*x for x in range(0,10)) //生成器, 這里是小括弧
//結果
二者的區別很明顯:
一個直接返回了表達式的結果列表, 而另一個是一個對象,該對象包含了對表達式結果的計算引用, 通過循環可以直接輸出
g = (x*x for x in range(0,10))
for n in g:
print n
結果
0
1
4
9
16
25
36
49
64
81
當表達式的結果數量較少的時候, 使用列表生成式還好, 一旦數量級過大, 那麼列表生成式就會佔用很大的內存,
而生成器並不是立即把結果寫入內存, 而是保存的一種計算方式, 通過不斷的獲取, 可以獲取到相應的位置的值,所以佔用的內存僅僅是對計算對象的保存
2. 浠涔堟槸python涓鐨勭敓鎴愬櫒鎺ㄥ煎紡
python涓鏈変竴縐嶇揣鍑戠殑璇娉曪紝鍙浠ラ氳繃涓涓寰鐜鍜屾潯浠舵瀯寤轟竴涓鍒楄〃錛岃繖縐嶈娉曞彨鍋氬垪琛ㄦ帹瀵煎紡錛坙ist comprehension錛夛細 my_list = [ f(x) for x in sequence if cond(x) ] 綾諱技鍦幫紝鎴戜滑鍙浠ラ氳繃瀛楀吀鎺ㄥ煎紡
python涓鏈変竴縐嶇揣鍑戠殑璇娉曪紝鍙浠ラ氳繃涓涓寰鐜鍜屾潯浠舵瀯寤轟竴涓鍒楄〃錛岃繖縐嶈娉曞彨鍋氬垪琛ㄦ帹瀵煎紡錛坙ist comprehension錛夛細
my_list = [ f(x) for x in sequence if cond(x) ]
綾諱技鍦幫紝鎴戜滑鍙浠ラ氳繃瀛楀吀鎺ㄥ煎紡錛坉ictionary comprehension錛夊壋寤哄瓧鍏革紝閫氳繃闆嗗悎鎺ㄥ煎紡錛坰et comprehension錛夊壋寤洪泦鍚堬細
my_dict = { k(x): v(x) for x in sequence if cond(x) }
my_set = { f(x) for x in sequence if cond(x) }
錛堣繖涓璇娉曟敮鎸佹洿鍔犲嶆潅鐨勬搷浣滐紝浣嗚繖閲屼粎浣滅ず渚嬶級
鏈鍚庯紝浣犺繕鍙浠ヤ嬌鐢ㄧ被浼肩殑璇娉曞壋寤轟竴涓鐢熸垚鍣錛
my_generator = ( f(x) for x in sequence if cond(x) )
涓嶈繃錛岃繖騫朵笉鍙鍋氱敓鎴愬櫒鎺ㄥ煎紡錛岃屾槸鍙鍋氱敓鎴愬櫒琛ㄨ揪寮忥紙generator expression錛夈備負浠涔堜笉鍙鍓嶈呭憿錛熷傛灉鍓嶄笁涓璇娉曢兘琚縐頒負鈥滄帹瀵煎紡鈥濓紝涓轟粈涔堢敓鎴愬櫒榪欎釜涓嶅彨鍛錛
pep 289 鈥斺 鐢熸垚鍣ㄨ〃杈懼紡 鐨勬渶鍚庣粰鍑轟簡璇︾粏鐨勫囨敞錛屽叾涓鎸囧嚭raymond hettinger璧峰垵鎻愯浣跨敤鈥滅敓鎴愬櫒鎺ㄥ煎紡錛坓enerator comprehension錛夆濅竴璇嶏紝鍚庢潵peter norvig鎻愬嚭浜嗏滅瘡璁℃樉紺猴紙accumulation displays錛夆濓紝鍚庢潵tim peters鎺ㄨ崘浜嗏滅敓鎴愬櫒琛ㄨ揪寮忊濊繖涓
鍚嶈瘝銆備絾鏄瀹冨苟娌℃湁鍚嶈瘝鍑虹幇浜嗚繖鏍風殑鍙樺寲銆
earlgrey錛氫笂闈㈡彁鍒扮殑榪欏嚑浣嶉兘鏄澶х墰鍟婏紒鍏蜂綋澶у跺彲浠ヨ胺姝屼竴涓嬨
鎵浠ユ垜鍦═witter涓婃彁鍑轟簡榪欎釜闂棰橈細
python 鏈変釜鎴戜笉鎳傜殑闂棰橈細涓轟粈涔堝畠浠琚縐頒負鈥滅敓鎴愬櫒琛ㄨ揪寮忊濓紝鑰屼笉鏄鈥滅敓鎴愬櫒鎺ㄥ煎紡鈥濓紵
guido鐨勫洖絳旀寚鍑轟簡鏍稿績鍘熷洜錛
鎺ㄥ煎紡涓寮濮嬪睘浜庘滃瓧闈㈤噺鏄劇ず錛坙iteral display錛夆濊繖涓姒傚康銆傝岀敓鎴愬櫒琛ㄨ揪寮忎笉鏄涓縐嶆樉紺猴紙display錛夈
matt boehm鍚庢潵鎵懼埌浜員im peters鎻愬嚭鈥滅敓鎴愬櫒琛ㄨ揪寮忊濅竴璇嶇殑閭浠訛紝鍏朵腑璁茶堪浜嗕竴浜涚粏鑺傦細
璇誨畬閭浠跺悗錛屾垜瀵硅繖涓闂棰樼殑鐞嗚в鏇存繁浜嗐傞栧厛錛屼負浠涔堜細浣跨敤鈥滄帹瀵煎紡鈥濓紙comprehension錛変竴璇嶏紵tim鍦ㄩ偖浠朵腑鎸囧嚭錛岃繖涓璇嶆潵婧愪簬闆嗗悎璁轟腑鐨勬帹瀵煎叕鐞嗭紙axiom of comprehension錛夛紝瀹冩寚鐨勬槸閫氳繃瀵瑰彟涓涓闆嗗悎鐨勫厓緔犲簲鐢ㄦ煇涓璋撹瘝錛坧redicate錛屽嵆鏉
浠訛級鑰岀粍鎴愭柊鐨勯泦鍚堛傝繖鍜屽悜鍙︿竴涓搴忓垪涓鐨勫厓緔犲簲鐢ㄦ煇涓鏉′歡浠庤岀敓鎴愬垪琛ㄧ殑鍋氭硶闈炲父綾諱技銆
earlgrey錛氭垜涔嬪墠鐪嬪埌寰堝氱炕璇戜負鈥滆в鏋愨濓紝鐪嬪埌榪欓噷鎵嶈夊緱鈥滄帹瀵煎紡鈥濇墠鏄鏇村噯紜鐨勮存硶銆
姝e侴uido鎵鎸囧嚭鐨勶紝python鐨勮捐¤呭綋鏃舵洿娉ㄩ噸鐨勬槸鏄劇ず錛岃屼笉鏄鏉′歡銆傗滄樉紺衡濅竴璇嶅湪榪欓噷鎰忓懗鐫浠g爜鐨勮娉曠湅涓婂拰瀹冨皢鍒涘緩鐨勬暟鎹緇撴瀯寰堝儚銆傚垪琛ㄦ樉紺猴紙鍒楄〃鎺ㄥ煎紡錛夌湅涓婂幓鍍忎竴涓鍒楄〃銆傚逛簬闆嗗悎鍜屽瓧鍏告樉紺烘潵璇達紝涔熸槸涓鏍風殑閬撶悊銆備絾鏄鐢變簬娌℃湁鐢熸垚鍣
瀛楅潰閲忚娉曪紝鍥犳ゆ牴鏈灝辨病鏈変竴涓鐢熸垚鍣ㄦ樉紺哄彲浠ヨ繘琛屽規瘮錛屼篃灝變笉瀛樺湪鐢熸垚鍣ㄦ樉紺轟簡銆
鍦ㄨ捐¤ュ姛鑳界殑閭e皝閭浠朵腑錛屸滄帹瀵煎紡鈥濅竴嬈℃槸鈥滄樉紺衡濈殑鍚屼箟璇嶏紝鐢變簬鐢熸垚鍣ㄦ病鏈夋樉紺猴紝鎵浠ヤ篃涓嶅彲鑳芥湁鎺ㄥ煎紡銆
涓嶈繃time鍦ㄤ粬鐨勯偖浠朵腑涔熻村埌錛屾帹瀵煎紡鐨勫囧欎箣澶勫湪浜庢潯浠躲傛帹瀵煎叕鐞嗙殑鏍稿績鍒欐槸璋撹銆備篃璁告槸鍥犱負python鎺ㄥ煎紡涓鐨勬潯浠舵槸鍙閫夌殑錛屽叧娉ㄧ殑鐒︾偣琚杞縐誨埌浜嗘樉紺烘柟闈銆
浣嗘槸鎴戣や負錛屾垜浠搴旇ュ彨瀹冧滑鈥滅敓鎴愬櫒鎺ㄥ煎紡鈥濄傛垜浠鍦ㄦ弿榪拌繖綾昏娉曟椂錛屽苟娌℃湁浣跨敤鈥滄樉紺衡濅竴璇嶃傛垜浠娌℃湁鐞嗙敱灝嗏滄帹瀵煎紡鈥濅笌鈥滄樉紺衡濆拰瀛楅潰閲忚娉曡仈緋誨湪涓璧楓
鍒楄〃鎺ㄥ煎紡銆佸瓧鍏告帹瀵煎紡銆侀泦鍚堟帹瀵煎紡鍜岀敓鎴愬櫒琛ㄨ揪寮忥紝榪欏洓涓琛ㄨ揪寮忓悇鑷涔嬮棿鏈夌潃璁稿氱浉浼間箣澶勩傚傛灉灝嗗洓鑰呬箣闂寸殑綾諱技鐐規葷粨涓衡滄帹瀵煎紡鈥濓紝灝嗘瀬澶у湴綆鍖栫浉鍏蟲傚康銆傚畠浠涔嬮棿鐨勭浉浼肩偣榪滃ぇ浜庝笉鍚屼箣澶勶紝鎴戝緩璁澶у跺硅繖鍥涗釜琛ㄨ揪寮忎嬌鐢ㄥ悓鏍風殑姒傚康
3. 如何更好地理解python迭代器和生成器
python這門語言中,生成器毫無疑問是最有用的特性之一。與此同時,也是使用的最不廣泛的python特
性之一。究其原因,主要是因為,在其他主流語言裡面沒有生成器的概念。正是由於生成器是一
個「新」的東西,所以,它一方面沒有引起廣大工程師的重視,另一方面,也增加了工程師的學習成本,
最終導致大家錯過了python中如此有用的一個特性。
我的這篇文章,希望通過簡單易懂的方式,深入淺出地介紹python的生成器,以改變「如此有用的特性卻
使用極不廣泛」的現象。本文的組織如下:在第1章,我們簡單地介紹了python中的迭代器協議;在本文
第2章,將會詳細介紹生成器的概念和語法;在第3章,將會給出一個有用的例子,說明使用生成器的好
處;在本文最後,簡單的討論了使用生成器的注意事項。
1. 迭代器協議
由於生成器自動實現了迭代器協議,而迭代器協議對很多人來說,也是一個較為抽象的概念。所以,為了
更好的理解生成器,我們需要簡單的回顧一下迭代器協議的概念。
1. 迭代器協議是指:對象需要提供next方法,它要麼返回迭代中的下一項,要麼就引起一個
stopiteration異常,以終止迭代
2. 可迭代對象就是:實現了迭代器協議的對象
3. 協議是一種約定,可迭代對象實現迭代器協議,python的內置工具(如for循環,sum,min,max函
數等)使用迭代器協議訪問對象。
舉個例子:在所有語言中,我們都可以使用for循環來遍歷數組,python的list底層實現是一個數組,所
以,我們可以使用for循環來遍歷list。如下所示:
>>> for n in [1, 2, 3, 4]:
... print n
但是,對python稍微熟悉一點的朋友應該知道,python的for循環不但可以用來遍歷list,還可以用來遍歷
文件對象,如下所示:
>>> with open(『/etc/passwd』) as f: # 文件對象提供迭代器協議
... for line in f: # for循環使用迭代器協議訪問文件
... print line
...
為什麼在python中,文件還可以使用for循環進行遍歷呢?這是因為,在python中,文件對象實現了迭代
器協議,for循環並不知道它遍歷的是一個文件對象,它只管使用迭代器協議訪問對象即可。正是由於
python的文件對象實現了迭代器協議,我們才得以使用如此方便的方式訪問文件,如下所示:
>>> f = open('/etc/passwd')
>>> dir(f)
['__class__', '__enter__', '__exit__', '__iter__', '__new__', 'writelines', '...'
2. 生成器
python使用生成器對延遲操作提供了支持。所謂延遲操作,是指在需要的時候才產生結果,而不是立即產
生結果。這也是生成器的主要好處。
python有兩種不同的方式提供生成器:
2017/11/6 如何更好地理解python迭代器和生成器? - 知乎
https://www.hu.com/question/20829330 2/5
1. 生成器函數:常規函數定義,但是,使用yield語句而不是return語句返回結果。yield語句一次返回一
個結果,在每個結果中間,掛起函數的狀態,以便下次重它離開的地方繼續執行
2. 生成器表達式:類似於列表推導,但是,生成器返回按需產生結果的一個對象,而不是一次構建一個
結果列表
2.1 生成器函數
我們來看一個例子,使用生成器返回自然數的平方(注意返回的是多個值):
def gensquares(n):
for i in range(n):
yield i ** 2
for item in gensquares(5):
print item,
使用普通函數:
def gensquares(n):
res = []
for i in range(n):
res.append(i*i)
return res
for item in gensquares(5):
print item,
可以看到,使用生成器函數代碼量更少。
2.2 生成器表達式
使用列表推導,將會一次產生所有結果:
>>> squares = [x**2 for x in range(5)]
>>> squares
[0, 1, 4, 9, 16]
將列表推導的中括弧,替換成圓括弧,就是一個生成器表達式:
>>> squares = (x**2 for x in range(5))
>>> squares>>> next(squares)
0
>>> next(squares)
1
>>> next(squares)
4
>>> list(squares)
[9, 16]
python不但使用迭代器協議,讓for循環變得更加通用。大部分內置函數,也是使用迭代器協議訪問對象
的。例如, sum函數是python的內置函數,該函數使用迭代器協議訪問對象,而生成器實現了迭代器協
2017/11/6 如何更好地理解python迭代器和生成器? - 知乎
https://www.hu.com/question/20829330 3/5
議,所以,我們可以直接這樣計算一系列值的和:
>>> sum(x ** 2 for x in xrange(4))
而不用多此一舉的先構造一個列表:
>>> sum([x ** 2 for x in xrange(4)])
2.3 再看生成器
前面已經對生成器有了感性的認識,我們以生成器函數為例,再來深入探討一下python的生成器:
1. 語法上和函數類似:生成器函數和常規函數幾乎是一樣的。它們都是使用def語句進行定義,差別在
於,生成器使用yield語句返回一個值,而常規函數使用return語句返回一個值
2. 自動實現迭代器協議:對於生成器,python會自動實現迭代器協議,以便應用到迭代背景中(如for
循環,sum函數)。由於生成器自動實現了迭代器協議,所以,我們可以調用它的next方法,並且,
在沒有值可以返回的時候,生成器自動產生stopiteration異常
3. 狀態掛起:生成器使用yield語句返回一個值。yield語句掛起該生成器函數的狀態,保留足夠的信息,
以便之後從它離開的地方繼續執行
3. 示例
我們再來看兩個生成器的例子,以便大家更好的理解生成器的作用。
首先,生成器的好處是延遲計算,一次返回一個結果。也就是說,它不會一次生成所有的結果,這對於大
數據量處理,將會非常有用。
大家可以在自己電腦上試試下面兩個表達式,並且觀察內存佔用情況。對於前一個表達式,我在自己的電
腦上進行測試,還沒有看到最終結果電腦就已經卡死,對於後一個表達式,幾乎沒有什麼內存佔用。
sum([i for i in xrange(10000000000)])
sum(i for i in xrange(10000000000))
除了延遲計算,生成器還能有效提高代碼可讀性。例如,現在有一個需求,求一段文字中,每個單詞出現
的位置。
不使用生成器的情況:
def index_words(text):
result = []
if text:
result.append(0)
for index, letter in enumerate(text, 1):
if letter == ' ':
result.append(index)
return result
使用生成器的情況:
2017/11/6 如何更好地理解python迭代器和生成器? - 知乎
https://www.hu.com/question/20829330 4/5
def index_words(text):
if text:
yield 0
for index, letter in enumerate(text, 1):
if letter == ' ':
yield index
這里,至少有兩個充分的理由說明 ,使用生成器比不使用生成器代碼更加清晰:
1. 使用生成器以後,代碼行數更少。大家要記住,如果想把代碼寫的pythonic,在保證代碼可讀性的前
提下,代碼行數越少越好
2. 不使用生成器的時候,對於每次結果,我們首先看到的是result.append(index),其次,才是index。
也就是說,我們每次看到的是一個列表的append操作,只是append的是我們想要的結果。使用生成
器的時候,直接yield index,少了列表append操作的干擾,我們一眼就能夠看出,代碼是要返回
index。
這個例子充分說明了,合理使用生成器,能夠有效提高代碼可讀性。只要大家完全接受了生成器的概念,
理解了yield語句和return語句一樣,也是返回一個值。那麼,就能夠理解為什麼使用生成器比不使用生成
器要好,能夠理解使用生成器真的可以讓代碼變得清晰易懂。
4. 使用生成器的注意事項
相信通過這篇文章,大家已經能夠理解生成器的作用和好處。但是,還沒有結束,使用生成器,也有一點
注意事項。
我們直接來看例子,假設文件中保存了每個省份的人口總數,現在,需要求每個省份的人口佔全國總人口
的比例。顯然,我們需要先求出全國的總人口,然後在遍歷每個省份的人口,用每個省的人口數除以總人
口數,就得到了每個省份的人口佔全國人口的比例。
如下所示:
def get_province_population(filename):
with open(filename) as f:
for line in f:
yield int(line)
gen = get_province_population('data.txt')
all_population = sum(gen)
#print all_population
for population in gen:
print population / all_population
執行上面這段代碼,將不會有任何輸出,這是因為,生成器只能遍歷一次。在我們執行sum語句的時候,
就遍歷了我們的生成器,當我們再次遍歷我們的生成器的時候,將不會有任何記錄。所以,上面的代碼不
會有任何輸出。
因此,生成器的唯一注意事項就是:生成器只能遍歷一次。
5. 總結
2017/11/6 如何更好地理解python迭代器和生成器? - 知乎
https://www.hu.com/question/20829330 5/5
本文深入淺出地介紹了python中,一個容易被大家忽略的重要特性,即python的生成器。為了講解生成
器,本文先介紹了迭代器協議,然後介紹了生成器函數和生成器表達式,並通過示例演示了生成器的優點
和注意事項。在實際工作中,充分利用python生成器,不但能夠減少內存使用,還能夠提高代碼可讀性。
掌握生成器也是python高手的標配。希望本文能夠幫助大家理解python的生成器