Python可迭代對象/迭代器/生成器
概述
迭代是數(shù)據(jù)處理的基石.
掃描內(nèi)存中放不下的數(shù)據(jù)集時, 需要找到一種惰性獲取數(shù)據(jù)的方式, 即需要按需一次獲取一個數(shù)據(jù)項. 這就是迭代器模式(Iterator pattern).
所有生成器都是迭代器, 因為生成器完全實現(xiàn)了迭代器接口.
在Python中所有集合都可迭代的. 在Python語言內(nèi)部, 迭代器用于支持:
- for循環(huán)
- 構(gòu)建和擴(kuò)展集合類型
- 逐行遍歷文本文件
- 列表推導(dǎo)/字典推導(dǎo)/集合推導(dǎo)
- 元組拆包
- 調(diào)用函數(shù)時, 使用*拆包實參
2.序列為何可以迭代?
解釋器需要迭代對象x時, 會自動調(diào)用iter(x)函數(shù).
內(nèi)置的iter函數(shù)有以下作用:
- 檢查對象是否實現(xiàn)了
__iter__
方法, 如果實現(xiàn)了就調(diào)用它, 獲取一個迭代器; - 如果該對象沒有實現(xiàn)
__iter__
方法, 但是實現(xiàn)了__getitem__
方法, Python會創(chuàng)建一個迭代器, 嘗試按順序(從索引0開始)獲取元素; - 如果嘗試失敗, Python會拋出
TypeError
異常, 通常會提示C object is not iterable
(C對象不可迭代), 其中C是目標(biāo)對象所屬的類.
由上可知: 所有的序列都可迭代的原因---都實現(xiàn)了__getitem__
方法.
標(biāo)準(zhǔn)的序列也都實現(xiàn)了
__iter__
方法, 因此我們也應(yīng)該這么做. 之所以對__getitem__
方法做特殊處理,是為了向后兼容.鴨子類型(duck typing)的極端形式: 不僅要實現(xiàn)特殊的
__iter__
方法,還要實現(xiàn)__getitem__
方法, 而且__getitem__
方法的參數(shù)是從0開始的整數(shù)(int), 才認(rèn)為對象是可迭代的.白鵝類型(goose-typing)理論中: 如果實現(xiàn)了
__iter__
方法, 那么就認(rèn)為對象是可迭代的.
檢查對象x是否可迭代, 最準(zhǔn)確的方法: 調(diào)用iter(x)
函數(shù), 如果不可迭代將拋出TypeError
異常, 再來處理此異常.
可迭代的對象與迭代器的對比
可迭代的對象: 使用iter
內(nèi)置函數(shù)可以獲取迭代器的對象.
- 如果對象實現(xiàn)了能返回迭代器的
__iter__
方法.- 序列對象.
- 實現(xiàn)了
__getitem__
方法, 而且其參數(shù)是從零開始的索引.
可迭代對象與迭代器關(guān)系: Python從可迭代對象中獲取迭代器.
StopIteration
異常表明迭代器到頭了.
Python語言內(nèi)部會處理for
循環(huán)和其他迭代器上下文(如列表推導(dǎo)/元組拆包,等等)中的StopIteration
異常.
標(biāo)準(zhǔn)迭代器接口:
-
__next__
: 返回下一個可用的元素, 如果沒有元素了, 拋出StopIteration
異常. -
__iter__
: 返回self
, 即迭代器實例本身. 以便在應(yīng)該使用可迭代對象的地方使用迭代器, 例如在for
循環(huán)中.
我們應(yīng)該避免調(diào)用類似上述這種特殊方法, 使用next()
即可.
檢查對象x是否是迭代器最好的辦法: 調(diào)用isinstance(x, abc.Iterator)
. 得益于Iterator.__subclasshook__
方法, 即使對象x所屬的類不是Iterator
類的真實子類或虛擬子類, 也能這樣檢查.
迭代器: 實現(xiàn)了無參數(shù)的__next__
方法, 返回序列中的下一個元素; 如果沒有元素了, 那么拋出StopIteration
異常.
Python中的迭代器實現(xiàn)了__iter__
方法, 因此迭代器也可以迭代.
因為內(nèi)置的
iter()
函數(shù)對序列做特殊處理. 接下來實現(xiàn)標(biāo)準(zhǔn)的迭代器協(xié)議
3.典型迭代器
# -*- coding: UTF-8 -*-
import re
import reprlib
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(text)
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def __iter__(self):
# 實現(xiàn)此方法: 表明此類可以迭代. 根據(jù)迭代器協(xié)議,__iter__方法實例并返回一個迭代器
return SentenceIterator(self.words)
class SentenceIterator:
def __init__(self, words):
self.words = words # 迭代器類引用單詞列表
self.index = 0 # index用于確定下一個要獲取的單詞
def __next__(self):
try:
word = self.words[self.index]
except IndexError:
raise StopIteration()
self.index += 1
return word
def __iter__(self):
return self
if __name__ == '__main__':
s = Sentence('"The time has come, " the Walrus said,')
print(s)
for word in s:
print(word)
print(list(s))
注意:
SentenceIterator
類的大多數(shù)代碼都在處理迭代器內(nèi)部狀態(tài).
可迭代對象實現(xiàn)了__iter__
方法, 每次都會實例化一個新的迭代器.
迭代器要實現(xiàn)__next__
方法, 調(diào)用next()
函數(shù)時返回單個元素, 還要實現(xiàn)__iter__
方法, 返回迭代器本身.
因此: 迭代器可以迭代, 但是可迭代的對象本身不是迭代器.
反模式: 在Sentence
類中實現(xiàn)__next__
方法, 讓其實例既是可迭代的對象, 也是自身的迭代器.
可迭代的對象一定不能是自身的迭代器.
也就是: 可迭代的對象必須實現(xiàn)
__iter__
方法, 但不能實現(xiàn)__next__
方法.另一方面: 迭代器應(yīng)該可以一直迭代. 迭代器的
__iter__
方法應(yīng)該返回自身.
迭代器模式可用來:
- 訪問一個聚合對象的內(nèi)容而無需暴露它的內(nèi)部表示
- 支持對聚合對象的多種遍歷
- 為遍歷不同的聚合結(jié)構(gòu)提供一個統(tǒng)一的接口(即支持多態(tài)迭代)
為了"支持多種遍歷", 必須能從同一個可迭代的實例中獲取多個獨立的迭代器, 而且各個迭代器要能維護(hù)自身的 內(nèi)部狀態(tài), 因此這一模式正確的實現(xiàn)方法是: 每次調(diào)用
iter(my_iterable)
都新建一個獨立的迭代器.這就是定義
SentenceIterator
類的原因.
4.生成器函數(shù)
# -*- coding: UTF-8 -*-
import re
import reprlib
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(text)
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def __iter__(self):
"""生成器函數(shù)"""
for word in self.words:
yield word
return # 此return不是必須的.可以直接不寫,直接返回None.不管有沒有,都不會拋出StopIteration異常.
if __name__ == '__main__':
s = Sentence('"The time has come, " the Walrus said,')
print(s)
for word in s:
print(word)
print(list(s))
生成器函數(shù): 只要Python函數(shù)的定義體中有yield
關(guān)鍵字, 該函數(shù)就是生成器函數(shù).
調(diào)用生成器函數(shù)時, 會返回一個生成器對象. 即: 生成器函數(shù)是生成器工廠.
執(zhí)行過程: 生成器函數(shù)會創(chuàng)建一個生成器對象, 包裝生成器函數(shù)的定義體. 把生成器傳給next()
函數(shù)時, 生成器函數(shù)會向前, 執(zhí)行函數(shù)定義體中的下一個yield
語句, 返回產(chǎn)出的值, 并在函數(shù)定義體的當(dāng)前位置暫停. 最終, 函數(shù)定義體返回時, 外層的生成器對象會拋出StopIteration
異常---與迭代器協(xié)議一致.
In [1]: def gen_123():
...: yield 1
...: yield 2
...: yield 3
...:
In [2]: gen_123
Out[2]: <function __main__.gen_123()>
In [3]: gen_123()
Out[3]: <generator object gen_123 at 0x10ce05c50>
In [4]: for i in gen_123():
...: print(i)
...:
1
2
3
In [5]: g = gen_123()
In [6]: next(g)
Out[6]: 1
In [7]: next(g)
Out[7]: 2
In [8]: next(g)
Out[8]: 3
In [9]: next(g)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-9-e734f8aca5ac> in <module>
----> 1 next(g)
StopIteration:
In [10]:
生成器不會以常規(guī)的方式"返回"值: 生成器函數(shù)定義體中的
return
語句會觸發(fā)生成器對象拋出StopIteration
異常.
In [10]: def gen_AB():
...: print("start")
...: yield 'A'
...: print("continue")
...: yield 'B'
...: print("end.")
...:
In [11]: for c in gen_AB():
...: print('-->', c)
...:
start
--> A
continue
--> B
end.
In [12]:
for
機(jī)制的作用與g = iter(gen_AB())
一樣, 用于獲取生成器對象, 然后每次迭代時調(diào)用next(g)
.
5.惰性實現(xiàn)
設(shè)計Iterator
接口時,考慮到了惰性: next(Iterator)
一次生成一個元素.
惰性求值(lazy evaluation)
及早求值(eager evaluation)
# -*- coding: UTF-8 -*-
import re
import reprlib
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self, text):
self.text = text
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def __iter__(self):
# finditer函數(shù)構(gòu)建一個迭代器,包含self.text中匹配RE_WORD的單詞,產(chǎn)出MatchObject實例
for match in RE_WORD.finditer(self.text):
# match.group()方法從MatchObject實例中提取匹配正則表達(dá)式的具體文本
yield match.group()
if __name__ == '__main__':
s = Sentence('"The time has come, " the Walrus said,')
print(s)
for word in s:
print(word)
print(list(s))
6.生成器表達(dá)式
生成器表達(dá)式可以理解為列表推導(dǎo)的惰性版本: 不會馬上創(chuàng)建列表, 而是返回一個生成器, 按需惰性生成元素.
即: 如果列表推導(dǎo)是制造列表的工廠, 那么生成器表達(dá)式就是制造生成器的工廠.
In [1]: def gen_AB():
...: print('start')
...: yield 'A'
...: print('continue')
...: yield 'B'
...: print('end.')
...:
In [2]: res1 = [x*3 for x in gen_AB()]
start
continue
end.
In [3]: for i in res1:
...: print('-->', i)
...:
--> AAA
--> BBB
In [4]: res2 = (x*3 for x in gen_AB())
In [5]: res2
Out[5]: <generator object <genexpr> at 0x110f66258>
In [6]: for i in res2: # for循環(huán)每次迭代時,會隱式調(diào)用next(res2),前進(jìn)到gen_AB函數(shù)的下一個yield語句
...: print('-->', i)
...:
start
--> AAA
continue
--> BBB
end.
由上可知,生成器表達(dá)式會產(chǎn)出生成器, 因此可以使用生成器表達(dá)式進(jìn)一步減少Sentence
類的代碼, 如下所示:
# -*- coding: UTF-8 -*-
import re
import reprlib
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self, text):
self.text = text
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def __iter__(self):
return (match.group() for match in RE_WORD.finditer(self.text))
if __name__ == '__main__':
s = Sentence('"The time has come, " the Walrus said,')
print(s)
for word in s:
print(word)
print(list(s))
生成器表達(dá)式是語法糖: 完全可以替換成生成器函數(shù), 不過有時使用生成器函數(shù)更便利.
7.何時使用生成器表達(dá)式
如果生成器表達(dá)式要分成多行寫, 可以選擇定義生成器函數(shù), 以便提高可讀性.
而且, 生成器函數(shù)有名稱, 可以重用.
句法提示: 如果函數(shù)或構(gòu)造方法只有一個參數(shù), 傳入生成器表達(dá)式時不用寫一對調(diào)用函數(shù)的括號, 再寫一對括號圍住生成器表達(dá)式, 只寫一對括號就行了. 然而, 如果生成器表達(dá)式后面還有其他參數(shù), 那么必須使用括號圍住,否則會拋出SyntaxError
異常.
8.等差數(shù)列生成器
典型的迭代器模式作用很簡單-----遍歷數(shù)據(jù)結(jié)構(gòu).
不過, 即便不是從集合中獲取元素, 而是獲取序列中即時生成的下一個值時, 也用得到這種基于方法的標(biāo)準(zhǔn)接口.
例如: 內(nèi)置的range
函數(shù)用于生成有窮整數(shù)等差數(shù)列(Arithmetic Progression, AP); itertools.count
函數(shù)用于生成無窮等差數(shù)列.
# -*- coding: UTF-8 -*-
class ArithmeticProgression:
def __init__(self, begin, step, end=None):
self.begin = begin
self.step = step
self.end = end
def __iter__(self):
# 將self.begin的值賦值給result,不過先強(qiáng)制轉(zhuǎn)換成前面的加法算式得到的類型.
result = type(self.begin + self.step)(self.begin)
forever = self.end is None
index = 0
while forever or result < self.end:
yield result
index += 1
result = self.begin + self.step * index
if __name__ == '__main__':
ap = ArithmeticProgression(0, 1, 3)
print(list(ap))
ap = ArithmeticProgression(1, .5, 3)
print(list(ap))
ap = ArithmeticProgression(0, 1/3, 1)
print(list(ap))
from fractions import Fraction
ap = ArithmeticProgression(0, Fraction(1, 3), 1)
print(list(ap))
from decimal import Decimal
ap = ArithmeticProgression(0, Decimal('.1'), .3)
print(list(ap))
上面這個類只是演示了如何使用生成器函數(shù)實現(xiàn)特殊的__iter__
方法.
然而, 如果一個類只是為了構(gòu)建生成器而去實現(xiàn)__iter__
方法, 還不如使用生成器函數(shù).
畢竟, 生成器函數(shù)是制造生成器的工廠.
# -*- coding: UTF-8 -*-
def aritprog_gen(begin, step, end=None):
result = type(begin + step)(begin)
forever = end is None
index = 0
while forever or result < end:
yield result
index += 1
result = begin + step * index
if __name__ == '__main__':
ap = aritprog_gen(0, 1, 3)
print(list(ap))
ap = aritprog_gen(1, .5, 3)
print(list(ap))
ap = aritprog_gen(0, 1/3, 1)
print(list(ap))
from fractions import Fraction
ap = aritprog_gen(0, Fraction(1, 3), 1)
print(list(ap))
from decimal import Decimal
ap = aritprog_gen(0, Decimal('.1'), .3)
print(list(ap))
上述即使用生成器函數(shù)來實現(xiàn)的.
使用itertools
模塊生成等差數(shù)列
itertools.count(start, step)
示例:
In [7]: import itertools
In [8]: gen = itertools.count(1, .5)
In [9]: next(gen)
Out[9]: 1
In [10]: next(gen)
Out[10]: 1.5
In [11]: next(gen)
Out[11]: 2.0
In [12]: next(gen)
Out[12]: 2.5
然而, itertools.count
函數(shù)從不停止. 如果調(diào)用list(count())
, Python會創(chuàng)建一個特別大的列表, 超出可用內(nèi)存, 在調(diào)用失敗之前, 電腦會瘋狂地運(yùn)轉(zhuǎn).
itertools.takewhile
函數(shù)則不同. 它會生成一個使用另一個生成器的生成器, 在指定的條件計算結(jié)果為False時停止. 因此, 可以把這兩個函數(shù)結(jié)合在一起使用. 如下所示:
In [13]: gen = itertools.takewhile(lambda n: n < 3, itertools.count(1, .5))
In [14]: list(gen)
Out[14]: [1, 1.5, 2.0, 2.5]
利用takewhile
和count
函數(shù), 編寫的代碼流暢而簡短, 如下所示:
# -*- coding: UTF-8 -*-
import itertools
def aritprog_gen(begin, step, end=None):
first = type(begin + step)(begin)
ap_gen = itertools.count(first, step)
if end is not None:
ap_gen = itertools.takewhile(lambda n: n < end, ap_gen)
return ap_gen
if __name__ == '__main__':
ap = aritprog_gen(0, 1, 3)
print(list(ap))
ap = aritprog_gen(1, .5, 3)
print(list(ap))
ap = aritprog_gen(0, 1/3, 1)
print(list(ap))
from fractions import Fraction
ap = aritprog_gen(0, Fraction(1, 3), 1)
print(list(ap))
from decimal import Decimal
ap = aritprog_gen(0, Decimal('.1'), .3)
print(list(ap))
上述示例中的aritprog_gen
不是生成器函數(shù), 因為定義體中并沒有yield
關(guān)鍵字.
但是它會返回一個生成器, 因此和其他生成器函數(shù)一樣, 也是生成器工廠函數(shù).
注意: 實現(xiàn)生成器時要知道標(biāo)準(zhǔn)庫中有什么可用, 否則很可能重復(fù)造輪子.
9.標(biāo)準(zhǔn)庫中的生成器函數(shù)
第一組是用于過濾的生成器函數(shù):從輸入的可迭代對象中產(chǎn)出元素的子集,而且不修改元素本身.
下表中的大多數(shù)函數(shù)都接受一個斷言參數(shù)(predicate). 這個參數(shù)是個布爾函數(shù), 有一個參數(shù), 會應(yīng)用到輸入中的每個元素上, 用于判斷元素是否包含在輸出中.
模塊 | 函數(shù) | 說明 |
---|---|---|
itertools |
compress(it, selector_it) |
并行處理兩個可迭代的對象; 如果selector_it 中的元素是真值, 產(chǎn)出it 中對應(yīng)的元素. |
itertools |
dropwhile(predicate, it) |
處理it , 跳過predicate 的計算結(jié)果為真值的元素, 然后產(chǎn)出剩下的各個元素(不再進(jìn)一步檢查) |
內(nèi)置 | filter(predicate, it) |
把it 中的每個元素傳給predicate , 如果返回真值, 那么產(chǎn)出對應(yīng)的元素; 如果predicate 為None, 那么只產(chǎn)出真值元素 |
itertools |
filterfalse(predicate, it) |
與filter 函數(shù)類似.不過是相反的: predicate 返回假值時產(chǎn)出對應(yīng)的元素 |
itertools |
islice(it, stop) 或islice(it, start, stop, step=1)
|
產(chǎn)出it 的切片, 作用類似于s[:stop] 或s[start:stop:step] , 不過it 可以是任何可迭代的對象, 而且這個函數(shù)實現(xiàn)的是惰性操作 |
itertools |
takewhile(predicate, it) |
predicate 返回真值時產(chǎn)出對應(yīng)的元素, 然后立即停止, 不再繼續(xù)檢查. |
示例如下:
In [15]: def vowel(c):
...: return c.lower() in 'aeiou'
...:
In [16]: list(filter(vowel, 'Aardvark'))
Out[16]: ['A', 'a', 'a']
In [17]: import itertools
In [18]: list(itertools.filterfalse(vowel, 'Aardvark'))
Out[18]: ['r', 'd', 'v', 'r', 'k']
In [19]: list(itertools.dropwhile(vowel, 'Aardvark'))
Out[19]: ['r', 'd', 'v', 'a', 'r', 'k']
In [20]: list(itertools.takewhile(vowel, 'Aardvark'))
Out[20]: ['A', 'a']
In [21]: list(itertools.compress('Aardvark', (1,0,1,1,0,1)))
Out[21]: ['A', 'r', 'd', 'a']
In [22]: list(itertools.islice('Aardvark', 4, 7))
Out[22]: ['v', 'a', 'r']
In [23]: list(itertools.islice('Aardvark', 1, 7, 2))
Out[23]: ['a', 'd', 'a']
下一組是用于映射的生成器函數(shù): 在輸入的單個可迭代對象(map
和starmap
函數(shù)處理多個可迭代的對象)中的各個元素上做計算, 然后返回結(jié)果.
下表中的生成器函數(shù)會從輸入的可迭代對象中的各個元素中產(chǎn)出一個元素. 如果輸入來自多個可迭代的對象, 第一個可迭代的對象到頭后就停止輸出.
模塊 | 函數(shù) | 說明 |
---|---|---|
itertools |
accumulate(it, [func] |
產(chǎn)出累積的總和; 如果提供了func , 那么把前兩個元素傳給它, 然后把計算結(jié)果和下一個元素傳給它, 以此類推, 最后產(chǎn)出結(jié)果. |
內(nèi)置 | enumerate(iterable, start=0) |
產(chǎn)出由兩個元素組成的元組, 結(jié)構(gòu)是(index, item) , 其中index 從start 開始計數(shù), item 則從iterable 中獲取 |
內(nèi)置 | map(func, it1, [it2,...,itN] |
把it 中的各個元素傳給func , 產(chǎn)出結(jié)果; 如果傳入N個可迭代的對象, 那么func 必須能接受N個參數(shù), 而且要并行處理各個可迭代的對象 |
itertools |
starmap(func, it) |
把it 中的各個元素傳給func , 產(chǎn)出結(jié)果; 輸入的可迭代對象應(yīng)該產(chǎn)出可迭代的元素iit , 然后以func(*iit) 這種形式調(diào)用func
|
itertools.accumulate
示例:
In [24]: sample = [5, 4, 2, 8, 7, 6, 3, 0, 9, 1]
In [25]: import itertools
In [26]: list(itertools.accumulate(sample)) # 計算總和
Out[26]: [5, 9, 11, 19, 26, 32, 35, 35, 44, 45]
In [27]: list(itertools.accumulate(sample, min)) # 計算最小值
Out[27]: [5, 4, 2, 2, 2, 2, 2, 0, 0, 0]
In [28]: list(itertools.accumulate(sample, max)) # 計算最大值
Out[28]: [5, 5, 5, 8, 8, 8, 8, 8, 9, 9]
In [29]: import operator
In [30]: list(itertools.accumulate(sample, operator.mul)) # 計算乘積
Out[30]: [5, 20, 40, 320, 2240, 13440, 40320, 0, 0, 0]
In [31]: list(itertools.accumulate(range(1, 11), operator.mul)) # 計算各數(shù)階乘
Out[31]: [1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]
映射生成器函數(shù)示例:
In [32]: list(enumerate('albatroz', 1)) # 以1開始,為單詞中的字母編號
Out[32]:
[(1, 'a'),
(2, 'l'),
(3, 'b'),
(4, 'a'),
(5, 't'),
(6, 'r'),
(7, 'o'),
(8, 'z')]
In [33]: import operator
In [34]: list(map(operator.mul, range(11), range(11))) # 從0到10,計算各個數(shù)的平方
Out[34]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
In [35]: list(map(operator.mul, range(11), [2, 4, 8])) # 元素最少的可迭代對象到頭后停止
Out[35]: [0, 4, 16]
In [36]: list(map(lambda x, y: (x, y), range(11), [2, 4, 8])) # 相當(dāng)于zip函數(shù)
Out[36]: [(0, 2), (1, 4), (2, 8)]
In [37]: list(itertools.starmap(operator.mul, enumerate('albatroz', 1)))
Out[37]: ['a', 'll', 'bbb', 'aaaa', 'ttttt', 'rrrrrr', 'ooooooo', 'zzzzzzzz']
In [38]: sample = [5, 4, 2, 8, 7, 6, 3, 0, 9, 1]
In [39]: list(itertools.starmap(lambda a, b: b/a, enumerate(itertools.accumulate(sample), 1)))
Out[39]:
[5.0,
4.5,
3.6666666666666665,
4.75,
5.2,
5.333333333333333,
5.0,
4.375,
4.888888888888889,
4.5]
下面一組是用于合并的生成器函數(shù), 這些函數(shù)都從輸入的多個可迭代對象中產(chǎn)出元素.
-
chain
和chain.from_iterable
按順序(一個接一個)處理輸入的可迭代對象 -
product
/zip
和zip_longest
并行處理輸入的各個可迭代對象.
模塊 | 函數(shù) | 說明 |
---|---|---|
itertools |
chain(it1, ..., itN) |
先產(chǎn)出it1 中的所有元素, 然后產(chǎn)出it2 中的所有元素, 以此類推, 無縫銜接在一起 |
itertools |
chain.from_iterable(it) |
產(chǎn)出it 生成的各個可迭代對象中的元素, 一個接一個, 無縫銜接; it 應(yīng)該產(chǎn)出可迭代的元素, 例如可迭代的對象列表 |
itertools |
product(it1, ..., itN, repeat=1) |
計算笛卡爾積: 從輸入的各個可迭代對象中獲取元素, 合并成由N個元素組成的數(shù)組, 與嵌套的for循環(huán)一樣; repeat 指明重復(fù)處理多少次輸入的可迭代對象 |
內(nèi)置 | zip(it1, ..., itN) |
并行從輸入的各個可迭代對象中獲取元素, 產(chǎn)出由N個元素組成的元組, 只要有一個可迭代對象到頭了, 就默默停止 |
itertools |
zip_longest(it1, ..., itN, fillvalue=None) |
并行從輸入的各個可迭代對象中獲取元素, 產(chǎn)出由N個元素組成的元組, 等到最長的可迭代對象到頭后停止, 空缺的值使用fillvalue 填充. |
zip
函數(shù)的名稱出自zip fastener
或zipper
(拉鏈, 與ZIP壓縮沒有關(guān)系).
chain
及zip
生成器函數(shù)及其同胞使用示例:
In [40]: import itertools
In [41]: list(itertools.chain('ABC', range(2))) # 通常傳入兩個或多個可迭代對象
Out[41]: ['A', 'B', 'C', 0, 1]
In [42]: list(itertools.chain(enumerate('ABC'))) # 如果僅傳入一個,則沒什么效果
Out[42]: [(0, 'A'), (1, 'B'), (2, 'C')]
In [43]: list(itertools.chain.from_iterable(enumerate('ABC')))
Out[43]: [0, 'A', 1, 'B', 2, 'C']
In [44]: list(zip('ABC', range(5))) # 常用于將兩個可迭代對象合并成一系列由兩個元素組成的元組
Out[44]: [('A', 0), ('B', 1), ('C', 2)]
In [45]: list(zip('ABC', range(5), [10, 20, 30, 40]))
Out[45]: [('A', 0, 10), ('B', 1, 20), ('C', 2, 30)]
In [46]: list(itertools.zip_longest('ABC', range(5)))
Out[46]: [('A', 0), ('B', 1), ('C', 2), (None, 3), (None, 4)]
In [47]: list(itertools.zip_longest('ABC', range(5), fillvalue='?'))
Out[47]: [('A', 0), ('B', 1), ('C', 2), ('?', 3), ('?', 4)]
10.yield from語法
如果生成器函數(shù)需要產(chǎn)出另一個生成器生成的值, 傳統(tǒng)解決方法是使用嵌套for循環(huán).
例如, 自己實現(xiàn)itertools.chain
:
In [1]: def chain(*iterable):
...: for it in iterable:
...: for i in it:
...: yield i
...:
In [2]: s = 'ABC'
In [3]: t = tuple(range(3))
In [4]: list(chain(s, t))
Out[4]: ['A', 'B', 'C', 0, 1, 2]
如果使用yield from
語法, 則是如下形式:
In [1]: def chain(*iterable):
...: for i in iterable:
...: yield from i
...:
In [2]: s = 'ABC'
In [3]: t = tuple(range(3))
In [4]: list(chain(s, t))
Out[4]: ['A', 'B', 'C', 0, 1, 2]
yield from
完全代替了內(nèi)層的for循環(huán).除了代替for循環(huán)外,
yield from
還會創(chuàng)建通道, 把內(nèi)層生成器直接與外層生成器的客戶端聯(lián)系起來.把生成器當(dāng)成協(xié)程使用時, 這個通道特別重要, 不僅能為客戶端代碼產(chǎn)生值, 還能使用客戶端代碼提供的值.
11.可迭代的歸約函數(shù)
歸約函數(shù)(合攏函數(shù)/累加函數(shù)): 接受一個可迭代對象, 返回單個結(jié)果.
其實,這里每個列出的函數(shù)都可以使用
itertools.reduce
函數(shù)實現(xiàn), 內(nèi)置是因為它們便于解決常見的問題.此外對于
all
和any
函數(shù)來說, 有一項優(yōu)化措施是reduce
函數(shù)做不到的: 短路(即一旦確定了結(jié)果就立即停止使用迭代器).
模塊 | 函數(shù) | 說明 |
---|---|---|
內(nèi)置 | all(it) |
it 中所有元素為真時返回True, 否則返回False; all([]) 返回True |
內(nèi)置 | any(it) |
只要it 中有元素為真值就返回True, 否則返回False; any([]) 返回False |
內(nèi)置 | max(it, [key=, ][default=]) |
返回it 中最大的元素; key是排序函數(shù), 與sorted函數(shù)中的一樣; 如果可迭代的對象為空, 返回default |
內(nèi)置 | min(it, [key=, ][default=]) |
返回it 中最小的元素; 其他同上 |
functools |
reduce(func, it, [initial]) |
把前兩個元素傳給func, 然后把計算結(jié)果和第三個元素傳給func, 以此類推, 返回最后的結(jié)果; 如果提供了initial, 把他當(dāng)做第一個元素傳入 |
內(nèi)置 | sum(it, start=0) |
it 中所有元素的和. 如果提供可選的start, 會把它加上 |
12.深入分析iter函數(shù)
鮮為人知的用法: 傳入兩個參數(shù), 使用常規(guī)的函數(shù)或任何可調(diào)用的對象創(chuàng)建迭代器.
- 第一個參數(shù)必須是可調(diào)用的對象, 用于不斷調(diào)用(沒有參數(shù))產(chǎn)出各個值
- 第二個參數(shù)是哨符, 是個標(biāo)記值, 當(dāng)可調(diào)用的對象返回這個值時, 觸發(fā)迭代器拋出
StopIteration
異常, 而不產(chǎn)出哨符.
示例如下:
In [1]: from random import randint
In [2]: def d6():
...: return randint(1, 6)
...:
In [3]: d6_iter = iter(d6, 1)
In [4]: d6_iter
Out[4]: <callable_iterator at 0x108c05c50>
In [5]: for roll in d6_iter:
...: print(roll)
...:
5
In [6]:
內(nèi)置函數(shù)iter
文檔中給了一個很好的例子: 逐行讀取文件, 直到遇到特定的行為止.
with open('mydata.txt') as fp:
for line in iter(fp.readline, ''):
process_line(line)