可迭代對象(Iterables)
??創(chuàng)建一個列表list時锅移,你可以逐個地讀取里面的每一項元素吕粗,這個過程稱之為迭代(iteration)</font>
>>> mylist = [1, 2, 3]
>>> for i in mylist:
... print(i)
1
2
3
mylist是一個可迭代的對象纺荧,當(dāng)使用列表推導(dǎo)式創(chuàng)建一個列表時,它就是可迭代對象。</font>
>>> mylist = [x*x for x in range(3)]
>>> for i in mylist:
... print(i)
0
1
4
任何可以使用在for...in...語句中的對象都叫做可迭代對象宙暇,例如:lists输枯、strings、files等等占贫。這些可迭代對象使用非常方便因為它們能如你所愿的盡可能的讀取其中的元素桃熄,但是你不得不把所有的值存儲在內(nèi)存中,當(dāng)它有大量元素的時候這并不一定總是你想要的型奥。
??dict對象已經(jīng)任何實現(xiàn)了_iter()或者_getitem()方法的類都是可迭代對象瞳收,此外,可迭代對象還可以用在zip厢汹、map等函數(shù)中螟深,當(dāng)一個可迭代對象作為參數(shù)傳遞給內(nèi)建函數(shù)iter()時,它會返回一個迭代器對象烫葬。通常沒有必要自己來處理迭代器本身或者手動調(diào)用iter()界弧,for語句會自動調(diào)用iter(),它會創(chuàng)建一個臨時的未命名的變量來持有這個迭代器用于循環(huán)期間搭综。</font>
迭代器(iterator)
??迭代器代表一個數(shù)據(jù)流對象夹纫,不斷重復(fù)調(diào)用迭代器的next()方法逐次的從返回數(shù)據(jù)流中的每一項,當(dāng)沒有更多數(shù)據(jù)可用時设凹,next()方法會拋出異常StopIteration。此時迭代器對象已經(jīng)枯竭了茅姜,之后調(diào)用next()方法都會拋出StopIteration闪朱。迭代器需要一個_iter_()方法用來返回迭代器本身,因此它也是一個可迭代的對象钻洒。</font>
生成器(Generators)
??生成器也是一個迭代器奋姿,但是你只可以迭代它們一次,不能重復(fù)迭代素标,因為它并沒有把所有的值存儲在內(nèi)存中称诗,而是實時地生成值:</font>
>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
... print(i)
0
1
4
從結(jié)果上看用()代替[]的效果是一樣的,但是你不可能第二次執(zhí)行for i in mygenerator,因為生成器只能使用一次</font>
Yield
??yield是關(guān)鍵字,它類似于return头遭,只是函數(shù)會返回一個生成器寓免。
>>> class Bank(): # 創(chuàng)建銀行,構(gòu)建ATM機计维,只要沒有危機袜香,就可以不斷地每次從中取100
... crisis = False
... def create_atm(self):
... while not self.crisis:
... yield "$100"
>>> hsbc = Bank() # when everything's ok the ATM gives you as much as you want
>>> corner_street_atm = hsbc.create_atm()
>>> print(corner_street_atm.next())
$100
>>> print(corner_street_atm.next())
$100
>>> print([corner_street_atm.next() for cash in range(5)])
['$100', '$100', '$100', '$100', '$100']
>>> hsbc.crisis = True # 危機來臨,沒有更多的錢了
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> wall_street_atm = hsbc.create_atm() # 即使創(chuàng)建一個新的ATM鲫惶,銀行還是沒錢
>>> print(wall_street_atm.next())
<type 'exceptions.StopIteration'>
>>> hsbc.crisis = False # 危機過后蜈首,銀行還是空的,因為該函數(shù)之前已經(jīng)不滿足while條件
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> brand_new_atm = hsbc.create_atm() # 必須構(gòu)建一個新的atm,恢復(fù)取錢業(yè)務(wù)
>>> for cash in brand_new_atm:
... print cash
$100
$100
$100
$100
$100
$100
$100
$100
$100
...
對于類似資源的訪問控制等場景欢策,生成器顯得很實用吆寨。
Itertools是你最好的朋友
??itertools模塊包含一些特殊的函數(shù)用來操作可迭代對象。曾經(jīng)想復(fù)制一個生成器踩寇?兩個生成器鏈接啄清?在內(nèi)嵌列表中一行代碼處理分組?不會創(chuàng)建另外一個列表的Map/Zip函數(shù)姑荷?你要做的就是import itertools 盒延。無例子無真相,我們來看看4匹馬賽跑到達終點所有可能的順序:
>>> horses = [1, 2, 3, 4]
>>> races = itertools.permutations(horses)
>>> print(races)
<itertools.permutations object at 0xb754f1dc>
>>> print(list(itertools.permutations(horses)))
[(1, 2, 3, 4),
(1, 2, 4, 3),
(1, 3, 2, 4),
(1, 3, 4, 2),
(1, 4, 2, 3),
(1, 4, 3, 2),
(2, 1, 3, 4),
(2, 1, 4, 3),
(2, 3, 1, 4),
(2, 3, 4, 1),
(2, 4, 1, 3),
(2, 4, 3, 1),
(3, 1, 2, 4),
(3, 1, 4, 2),
(3, 2, 1, 4),
(3, 2, 4, 1),
(3, 4, 1, 2),
(3, 4, 2, 1),
(4, 1, 2, 3),
(4, 1, 3, 2),
(4, 2, 1, 3),
(4, 2, 3, 1),
(4, 3, 1, 2),
(4, 3, 2, 1)]
理解迭代器的內(nèi)部機理
??迭代是操作可迭代對象(實現(xiàn)了_iter()方法)和迭代器(實現(xiàn)了_next()方法)的過程鼠冕√硭拢可迭代對象是任何你可以從其中得到一個迭代器對象的任意對象(調(diào)用內(nèi)建函數(shù)_iter_()),迭代器是能讓你在可迭代對象上進行迭代的對象懈费。
??一個帶有 yield 的函數(shù)就是一個 generator计露,它和普通函數(shù)不同,生成一個 generator 看起來像函數(shù)調(diào)用憎乙,但不會執(zhí)行任何函數(shù)代碼票罐,直到對其調(diào)用 next()(在 for 循環(huán)中會自動調(diào)用 next())才開始執(zhí)行。雖然執(zhí)行流程仍按函數(shù)的流程執(zhí)行泞边,但每執(zhí)行到一個 yield 語句就會中斷该押,并返回一個迭代值,下次執(zhí)行時從 yield 的下一個語句繼續(xù)執(zhí)行阵谚〔侠瘢看起來就好像一個函數(shù)在正常執(zhí)行的過程中被 yield 中斷了數(shù)次,每次中斷都會通過 yield 返回當(dāng)前的迭代值梢什。
yield 的好處是顯而易見的奠蹬,把一個函數(shù)改寫為一個 generator 就獲得了迭代能力,比起用類的實例保存狀態(tài)來計算下一個 next() 的值嗡午,不僅代碼簡潔囤躁,而且執(zhí)行流程異常清晰。
使用Yield的一個例子荔睹,從文件讀取內(nèi)容狸演。
如果直接對文件 對象調(diào)用read()方法,會導(dǎo)致不可預(yù)測的內(nèi)存占用僻他。好的方法是利用固定長度的緩沖區(qū)來不斷讀取文件內(nèi)容严沥。通過yield,我們不在需要編寫讀取文件的迭代類中姜,就可以輕松實現(xiàn)文件讀认:
def read_file(fpath):
BLOCK_SIZE = 1024
with open(fpath, 'rb') as f:
while True:
block = f.read(BLOCK_SIZE)
if block:
yield block
else:
return