在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Python/ 迭代器
附錄
進(jìn)程通信
操作系統(tǒng)
迭代器
模塊
描述符
裝飾器
第三部分 擴(kuò)展庫
內(nèi)置類型
數(shù)據(jù)存儲(chǔ)
數(shù)據(jù)類型
基本環(huán)境
文件與目錄
異常
程序框架
數(shù)學(xué)運(yùn)算
函數(shù)
元類
字符串
表達(dá)式

迭代器

在 Python 文檔中,實(shí)現(xiàn)接口通常被稱為遵守協(xié)議。因?yàn)?"弱類型" 和 "Duck Type" 的緣故,很多靜態(tài)語言中繁復(fù)的模式被悄悄抹平。

迭代器

迭代器協(xié)議,僅需要 iter() 和 next() 兩個(gè)方法。前者返回迭代器對(duì)象,后者依次返回?cái)?shù)據(jù),直到引發(fā) StopIteration 異常結(jié)束。

最簡單的做法是用內(nèi)置函數(shù) iter(),它返回常用類型的迭代器包裝對(duì)象。問題是,序列類型已經(jīng)可以被 for 處理,為何還要這么做?

>>> class Data(object):
...     def __init__(self):
...         self._data = []
...
...     def add(self, x):
...         self._data.append(x)
...
...     def data(self):
...         return iter(self._data)

>>> d = Data()

>>> d.add(1)
>>> d.add(2)
>>> d.add(3)

>>> for x in d.data(): print x
1
2
3

返回迭代器對(duì)象代替 self._data 列表,可避免對(duì)象狀態(tài)被外部修改?;蛟S你會(huì)嘗試返回 tuple,但這需要復(fù)制整個(gè)列表,浪費(fèi)更多的內(nèi)存。

iter() 很方便,但無法讓迭代中途停止,這需要自己動(dòng)手實(shí)現(xiàn)迭代器對(duì)象。在設(shè)計(jì)原則上,通常會(huì)將迭代器從數(shù)據(jù)對(duì)象中分離出去。因?yàn)榈餍枰S持狀態(tài),且可能有多個(gè)迭代器在同時(shí)操控?cái)?shù)據(jù),這些不該成為數(shù)據(jù)對(duì)象的負(fù)擔(dān),無端提升了復(fù)雜度。

>>> class Data(object):
...     def __init__(self, *args):
...         self._data = list(args)
...
...     def __iter__(self):
...         return DataIter(self)

>>> class DataIter(object):
...     def __init__(self, data):
...         self._index = 0
...         self._data = data._data
...
...     def next(self):
...         if self._index >= len(self._data): raise StopIteration()
...         d = self._data[self._index]
...         self._index += 1
...         return d

>>> d = Data(1, 2, 3)

>>> for x in d: print x
1
2
3

Data 僅僅是數(shù)據(jù)容器,只需 iter 返回迭代器對(duì)象,而由 DataIter 提供 next 方法。

除了 for 循環(huán),迭代器也可以直接用 next() 操控。

>>> d = Data(1, 2, 3)

>>> it = iter(d)
>>> it
<__main__.DataIter object at 0x10dafe850>

>>> next(it)
1
>>> next(it)
2
>>> next(it)
3

>>> next(it)
StopIteration

生成器

基于索引實(shí)現(xiàn)的迭代器有些丑陋,更合理的做法是用 yield 返回實(shí)現(xiàn)了迭代器協(xié)議的 Generator 對(duì)象。

>>> class Data(object):
...     def __init__(self, *args):
...         self._data = list(args)
...
...     def __iter__(self):
...         for x in self._data:
...             yield x

>>> d = Data(1, 2, 3)

>>> for x in d: print x
1
2
3

編譯器魔法會(huì)將包含 yield 的方法 (或函數(shù)) 重新打包,使其返回 Generator 對(duì)象。這樣一來,就無須廢力氣維護(hù)額外的迭代器類型了。

>>> d.__iter__()
<generator object __iter__ at 0x10db01280>

>>> iter(d).next()
1

協(xié)程

yield 為何能實(shí)現(xiàn)這樣的魔法?這涉及到協(xié)程 (coroutine) 的工作原理。先看下面的例子。

>>> def coroutine():
...     print "coroutine start..."
...     result = None
...     while True:
...         s = yield result
...         result = s.split(",")

>>> c = coroutine()   # 函數(shù)返回協(xié)程對(duì)象。

>>> c.send(None)    # 使用 send(None) 或 next() 啟動(dòng)協(xié)程。
coroutine start...

>>> c.send("a,b")    # 向協(xié)程發(fā)送消息,使其恢復(fù)執(zhí)行。
['a', 'b']

>>> c.send("c,d")
['c', 'd']

>>> c.close()    # 關(guān)閉協(xié)程,使其退出?;蛴?c.throw() 使其引發(fā)異常。
>>> c.send("e,f")    # 無法向已關(guān)閉的協(xié)程發(fā)送消息。
StopIteration

協(xié)程執(zhí)行流程:

  • 創(chuàng)建協(xié)程后對(duì)象,必須使用 send(None) 或 next() 啟動(dòng)。
  • 協(xié)程在執(zhí)行 yield result 后讓出執(zhí)行緒,等待消息。
  • 調(diào)用方發(fā)送 send("a,b") 消息,協(xié)程恢復(fù)執(zhí)行,將接收到的數(shù)據(jù)保存到 s,執(zhí)行后續(xù)流程。
  • 再次循環(huán)到 yeild,協(xié)程返回前面的處理結(jié)果,并再次讓出執(zhí)行緒。
  • 直到關(guān)閉或被引發(fā)異常。

close() 引發(fā)協(xié)程 GeneratorExit 異常,使其正常退出。而 throw() 可以引發(fā)任何類型的異常,這需要在協(xié)程內(nèi)部捕獲。

雖然生成器 yield 能輕松實(shí)現(xiàn)協(xié)程機(jī)制,但離真正意義上的高并發(fā)還有不小的距離。可以考慮使用成熟的第三方庫,比如 gevent/eventlet,或直接用 greenlet。

模式

善用迭代器,總會(huì)有意外的驚喜。

生產(chǎn)消費(fèi)模型

利用 yield 協(xié)程特性,我們無需多線程就可以編寫生產(chǎn)消費(fèi)模型。

>>> def consumer():
...     while True:
...         d = yield
...         if not d: break
...         print "consumer:", d

>>> c = consumer()  # 創(chuàng)建消費(fèi)者
>>> c.send(None)  # 啟動(dòng)消費(fèi)者

>>> c.send(1)  # 生產(chǎn)數(shù)據(jù),并提交給消費(fèi)者。
consumer: 1

>>> c.send(2)
consumer: 2

>>> c.send(3)
consumer: 3

>>> c.send(None)  # 生產(chǎn)結(jié)束,通知消費(fèi)者結(jié)束。
StopIteration

改進(jìn)回調(diào)

回調(diào)函數(shù)是實(shí)現(xiàn)異步操作的常用手法,只不過代碼規(guī)模一大,看上去就不那么舒服了。好好的邏輯被切分到兩個(gè)函數(shù)里,維護(hù)也是個(gè)問題。有了 yield,完全可以用 blocking style 編寫異步調(diào)用。

下面是 callback 版本的示例,其中 Framework 調(diào)用 logic,在完成某些操作或者接收到信號(hào)后,用 callback 返回異步結(jié)果。

>>> def framework(logic, callback):
...     s = logic()
...     print "[FX] logic: ", s
...     print "[FX] do something..."
...     callback("async:" + s)

>>> def logic():
...     s = "mylogic"
...     return s

>>> def callback(s):
...     print s

>>> framework(logic, callback)
[FX] logic: mylogic
[FX] do something...
async:mylogic

看看用 yield 改進(jìn)的 blocking style 版本。

>>> def framework(logic):
...     try:
...         it = logic()
...         s = next(it)
...         print "[FX] logic: ", s
...         print "[FX] do something"
...         it.send("async:" + s)
...     except StopIteration:
...         pass

>>> def logic():

...     s = "mylogic"
...     r = yield s
...     print r

>>> framework(logic)
[FX] logic: mylogic
[FX] do something
async:mylogic

盡管 framework 變得復(fù)雜了一些,但卻保持了 logic 的完整性。blocking style 樣式的編碼給邏輯維護(hù)帶來的好處無需言說。

寶藏

標(biāo)準(zhǔn)庫 itertools 模塊是不應(yīng)該忽視的寶藏。

chain

連接多個(gè)迭代器。

>>> it = chain(xrange(3), "abc")
>>> list(it)
[0, 1, 2, 'a', 'b', 'c']

combinations

返回指定長度的元素順序組合序列。

>>> it = combinations("abcd", 2)
>>> list(it)
[('a', 'b'), ('a', 'c'), ('a', 'd'), ('b', 'c'), ('b', 'd'), ('c', 'd')]

>>> it = combinations(xrange(4), 2)
>>> list(it)
[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]

combinations_with_replacement 會(huì)額外返回同一元素的組合。

>>> it = combinations_with_replacement("abcd", 2)
>>> list(it)
[('a', 'a'), ('a', 'b'), ('a', 'c'), ('a', 'd'), ('b', 'b'), ('b', 'c'), ('b', 'd'),
('c', 'c'), ('c', 'd'), ('d', 'd')]

compress

按條件表過濾迭代器元素。

>>> it = compress("abcde", [1, 0, 1, 1, 0])
>>> list(it)
['a', 'c', 'd']

條件列表可以是任何布爾列表。

count

從起點(diǎn)開始,"無限" 循環(huán)下去。

>>> for x in count(10, step = 2):
...     print x
...     if x > 17: break

10
12
14
16
18

cycle

迭代結(jié)束,再從頭來過。

>>> for i, x in enumerate(cycle("abc")):
...     print x
...     if i > 7: break

a
b
c
a
b
c
a
b
c

dropwhile

跳過頭部符合條件的元素。

>>> it = dropwhile(lambda i: i < 4, [2, 1, 4, 1, 3])
>>> list(it)
[4, 1, 3]

takewhile 則僅保留頭部符合條件的元素。

>>> it = takewhile(lambda i: i < 4, [2, 1, 4, 1, 3])
>>> list(it)
[2, 1]

groupby

將連續(xù)出現(xiàn)的相同元素進(jìn)行分組。

>>> [list(k) for k, g in groupby('AAAABBBCCDAABBCCDD')]
[['A'], ['B'], ['C'], ['D'], ['A'], ['B'], ['C'], ['D']]

>>> [list(g) for k, g in groupby('AAAABBBCCDAABBCCDD')]
[['A', 'A', 'A', 'A'], ['B', 'B', 'B'], ['C', 'C'], ['D'], ['A', 'A'], ['B', 'B'], ['C',
'C'], ['D', 'D']]

ifilter

與內(nèi)置函數(shù) filter() 類似,僅保留符合條件的元素。

>>> it = ifilter(lambda x: x % 2, xrange(10))
>>> list(it)
[1, 3, 5, 7, 9]

ifilterfalse 正好相反,保留不符合條件的元素。

>>> it = ifilterfalse(lambda x: x % 2, xrange(10))
>>> list(it)
[0, 2, 4, 6, 8]

imap

與內(nèi)置函數(shù) map() 類似。

>>> it = imap(lambda x, y: x + y, (2,3,10), (5,2,3))
>>> list(it)
[7, 5, 13]

islice

以切片的方式從迭代器獲取元素。

>>> it = islice(xrange(10), 3)
>>> list(it)
[0, 1, 2]

>>> it = islice(xrange(10), 3, 5)
>>> list(it)
[3, 4]

>>> it = islice(xrange(10), 3, 9, 2)
>>> list(it)
[3, 5, 7]

izip

與內(nèi)置函數(shù) zip() 類似,多余元素會(huì)被拋棄。

>>> it = izip("abc", [1, 2])
>>> list(it)
[('a', 1), ('b', 2)]

要保留多余元素可以用 izip_longest,它提供了一個(gè)補(bǔ)缺參數(shù)。

>>> it = izip_longest("abc", [1, 2], fillvalue = 0)
>>> list(it)
[('a', 1), ('b', 2), ('c', 0)]

permutations

與 combinations 順序組合不同,permutations 讓每個(gè)元素都從頭組合一遍。

>>> it = permutations("abc", 2)
>>> list(it)
[('a', 'b'), ('a', 'c'), ('b', 'a'), ('b', 'c'), ('c', 'a'), ('c', 'b')]

>>> it = combinations("abc", 2)
>>> list(it)
[('a', 'b'), ('a', 'c'), ('b', 'c')]

product

讓每個(gè)元素都和后面的迭代器完整組合一遍。

>>> it = product("abc", [0, 1])
>>> list(it)
[('a', 0), ('a', 1), ('b', 0), ('b', 1), ('c', 0), ('c', 1)]

repeat

將一個(gè)對(duì)象重復(fù) n 次。

>>> it = repeat("a", 3)
>>> list(it)
['a', 'a', 'a']

starmap

按順序處理每組元素。

>>> it = starmap(lambda x, y: x + y, [(1, 2), (10, 20)])
>>> list(it)
[3, 30]

tee

復(fù)制迭代器。

>>> for it in tee(xrange(5), 3):
... print list(it)

[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4]
上一篇:內(nèi)置類型下一篇:模塊