日韩久久久精品,亚洲精品久久久久久久久久久,亚洲欧美一区二区三区国产精品 ,一区二区福利

在Python中使用itertools模塊中的組合函數(shù)的教程

系統(tǒng) 1768 0

理解新概念

Python V2.2 中引入了迭代器的思想。唔,這并不十分正確;這種思想的“苗頭”早已出現(xiàn)在較老的函數(shù) xrange() 以及文件方法 .xreadlines() 中了。通過引入 yield 關(guān)鍵字,Python 2.2 在內(nèi)部實現(xiàn)的許多方面推廣了這一概念,并使編程定制迭代器變得更為簡單( yield 的出現(xiàn)使函數(shù)轉(zhuǎn)換成生成器,而生成器反過來又返回迭代器)。

迭代器背后的動機有兩方面。將數(shù)據(jù)作為序列處理通常是最簡單的方法,而以線性順序處理的序列通常并不需要都同時實際 存在。

x*() 前兆提供了這些原理的清晰示例。如果您想對某操作執(zhí)行成千上萬次,那么執(zhí)行您的程序可能要花些時間,但該程序一般不需要占用大量內(nèi)存。同樣,對于許多類型的文件,可以一行一行地處理,且不需要將整個文件存儲在內(nèi)存中。最好對其它所有種類的序列也進行惰性處理;它們可能依賴于通過通道逐步到達的數(shù)據(jù),或者依賴于一步一步執(zhí)行的計算。

大多數(shù)時候,迭代器用在 for 循環(huán)內(nèi),就象真正的序列那樣。迭代器提供了 .next() 方法,它可以被顯式調(diào)用,但有百分之九十九的可能,您所看到的是以下行:

            
for x in iterator:
  do_something_with(x)


          

在對 iterator.next() 進行幕后調(diào)用而產(chǎn)生 StopIteration 異常時,該循環(huán)就被終止。順便說一下,通過調(diào)用 iter(seq) ,實際序列可以被轉(zhuǎn)換成迭代器 - 這不會節(jié)省任何內(nèi)存,但是在下面討論的函數(shù)中它會很有用。

Python 不斷發(fā)展的分裂性格

Python 對函數(shù)編程(FP)的觀點有點相互矛盾。一方面,許多 Python 開發(fā)人員輕視傳統(tǒng)的 FP 函數(shù) map() 、 filter() 和 reduce() ,常常建議使用“列表理解”來代替它們。但完整的 itertools 模塊恰恰是由與這些函數(shù)類型完全相同的函數(shù)組成的,只不過這些函數(shù)對“惰性序列”(迭代器)操作,而不是對完整的序列(列表,元組)操作。而且,Python 2.3 中沒有任何“迭代器理解”的語法,這似乎與列表理解擁有一樣的動機。

我猜想 Python 最終會產(chǎn)生某種形式的迭代器理解,但這取決于找到合適于它們的自然語法。同時,在 itertools 模塊中,我們擁有大量有用的組合函數(shù)。大致地,這些函數(shù)中的每一個都接受一些參數(shù)(通常包含一些基礎(chǔ)迭代器)并返回一個新迭代器。例如,函數(shù) ifilter() 、 imap() 和 izip() 都分別直接等同于缺少詞首 i 的內(nèi)置函數(shù)。

缺少的等價函數(shù)

itertools 中沒有 ireduce() ,盡管按道理很自然地應該有這個函數(shù);可能的 Python 實現(xiàn)是:
清單 1. ireduce() 的樣本實現(xiàn)

            
def ireduce(func, iterable, init=None):
  if init is None:
    iterable = iter(iterable)
    curr = iterable.next()
  else:
    curr = init
  for x in iterable:
    curr = func(curr, x)
    yield curr


          

ireduce() 的用例類似于 reduce() 的用例。例如,假設(shè)您想要添加某個大型文件所包含的一列數(shù)字,但是當滿足一個條件時就停止。您可以使用以下代碼來監(jiān)控正在計算的合計數(shù):
清單 2. 添加并合計一列數(shù)

            
from operator import add
from itertools import *
nums = open('numbers')
for tot in takewhile(condition, ireduce(add, imap(int, nums)):
  print "total =", tot


          

一個更實際的示例可能類似于將事件流應用于有狀態(tài)對象上,例如應用到 GUI 窗口小部件上。但是即使是上述簡單示例也顯示了迭代器組合器的 FP 風格。

基本迭代器工廠

itertools 中的所有函數(shù)都可以用純 Python 輕松地實現(xiàn)為生成器。在 Python 2.3+ 中包含該模塊的要點是為一些有用的函數(shù)提供標準行為和名稱。盡管程序員可以編寫他們自己的版本,但是每個人實際創(chuàng)建的變體都會有點不兼容。但是,另一方面是要以高效率的 C 代碼實現(xiàn)迭代器組合器。使用 itertools 函數(shù)將比編寫您自己的組合器稍微快一些。標準文檔顯示了每個 itertools 函數(shù)的等價純 Python 實現(xiàn),所以不需要在本文中重復這些內(nèi)容了。

itertools 中的函數(shù)再基本不過了 - 而且命名也完全不同 - 這樣從該模塊導入所有名稱可能就有意義了。例如,函數(shù) enumerate() 可能明顯地出現(xiàn)在 itertools 中,但是它在 Python 2.3+ 中卻是一個內(nèi)置函數(shù)。值得注意的是,您可以用 itertools 函數(shù)很方便地表達 enumerate() :

            
from itertools import *
enumerate = lambda iterable: izip(count(), iterable)


          

讓我們首先看一下幾個 itertools 函數(shù),它們 沒有將其它迭代器作為基礎(chǔ),而完全是“從頭”創(chuàng)建迭代器。 times() 返回一個多次產(chǎn)生同一對象的迭代器;在本質(zhì)上,這一能力比較有用,但它確實可以很好地替代使用過多的 xrange() 和索引變量,從而可以簡單地重復一個操作。即,不必使用:

            
for i in xrange(1000):
  do_something()


          

您現(xiàn)在就可以使用更中性的:

            
for _ in times(1000):
  do_something()


          

如果 times() 只有一個參數(shù),那么它只會重復產(chǎn)生 None 。函數(shù) repeat() 類似于 times() ,但它無界地返回同一對象。不管是在包含獨立 break 條件的循環(huán)中還是在象 izip() 和 imap() 這樣的組合器中,這個迭代器都很有用。

函數(shù) count() 有點類似于 repeat() 和 xrange() 的交叉。 count() 無界地返回連續(xù)整數(shù)(以可選的參數(shù)為開始)。但是,如果 count() 當前不支持溢出到現(xiàn)在正確的 longs,那么您可能還是要使用 xrange(n,sys.maxint) ;它并不是完全無界的,但是對于大多數(shù)用途,它實際上是一回事。類似于 repeat() , count() 在其它迭代器組合器內(nèi)部特別有用。

組合函數(shù)

我們已經(jīng)順便提到了 itertools 中的幾個實際組合函數(shù)。 ifilter() 、 izip() 和 imap() 的作用就象您會期望從它們相應的序列函數(shù)上獲得的作用。 ifilterfalse() 很方便,所以您不需要去掉 lambda 和 def 中的謂詞函數(shù)(而且這還節(jié)省了大量的函數(shù)調(diào)用開銷)。但是在功能上,您可以將 ifilterfalse() 定義為(大致的情況,忽略了 None 謂詞):

            
def ifilterfalse(predicate, iterable):
  return ifilter(lambda predicate: not predicate, iterable)


          

函數(shù) dropwhile() 和函數(shù) takewhile() 根據(jù)謂詞對迭代器進行劃分。 dropwhile() 在直到滿足某個謂詞 之前忽略所產(chǎn)生的元素, takewhile() 在滿足某個謂詞 時就終止。 dropwhile() 跳過迭代器的不定數(shù)目的初始元素,所以它可能直到某個延遲后才開始迭代。 takewhile() 馬上開始迭代,但是如果被傳入的謂詞變?yōu)檎妫敲淳徒K止迭代器。

函數(shù) islice() 基本上就是列表分片的迭代器版本。您可以指定開始、停止和步長,就象使用常規(guī)的片。如果給定了開始,那么會刪除大量元素,直到被傳遞的迭代器到達滿足條件的元素為止。這是另一個我認為可以對 Python 進行改進的情形 - 迭代器最好只識別片,就象列表所做的(作為 islice() 行為的同義詞)。

最后一個函數(shù) starmap() 在 imap() 基礎(chǔ)上略微有些變化。如果這個作為參數(shù)傳遞的函數(shù)獲取多個參數(shù),那么被傳遞的 iterable 會產(chǎn)生大小適合的元組。這基本上與包含多個被傳入 iterable 的 imap() 相同,只不過它包含先前與 izip() 結(jié)合在一起的 iterables 集合。

深入探討

itertools 中包含的函數(shù)是一個很好的開始。不使用其它函數(shù),只用這些函數(shù)就可以讓 Python 程序員更輕松地利用和組合迭代器。一般說來,迭代器的廣泛使用對 Python 的未來無疑是很重要的。但是除了過去所包含的內(nèi)容以外,我還要對該模塊的將來更新提幾點建議。您可以立即很方便地使用這些函數(shù) - 當然,如果它們是后來被包含進來的,那么名稱或接口細節(jié)會有所不同。

一種可能會很通用的類別是一些將多個 iterable 作為參數(shù),隨后從每個 iterable 產(chǎn)生單獨元素的函數(shù)。與此相對照的是, izip() 產(chǎn)生元素元組,而 imap() 產(chǎn)生從基本元素計算而來的值。我頭腦中很清晰的兩個安排是 chain() 和 weave() 。第一個在效果上類似于序列并置(但是有點惰性)。即,在您可能使用的純序列中,例如:

            
for x in ('a','b','c') + (1, 2, 3):
  do_something(x)


          

對于一般的 iterables,您可以使用:

            
for x in chain(iter1, iter2, iter3):
  do_something(x)


          

Python 實現(xiàn)是:
清單 3. chain() 的樣本實現(xiàn)

            
def chain(*iterables):
  for iterable in iterables:
    for item in iterable:
      yield item


          

使用 iterables,您還可以通過使它們分散排列來組合幾個序列。還沒有任何對序列執(zhí)行這樣相同操作的內(nèi)置語法,但是 weave() 本身也非常適用于完整的序列。下面是可能的實現(xiàn)(Magnus Lie Hetland 提出了 comp.lang.python 的類似函數(shù)):
清單 4. weave() 的樣本實現(xiàn)

            
def weave(*iterables):
  "Intersperse several iterables, until all are exhausted"
  iterables = map(iter, iterables)
  while iterables:
    for i, it in enumerate(iterables):
      try:
        yield it.next()
      except StopIteration:
        del iterables[i]


          

讓我來演示一下 weave() 的行為,因為從實現(xiàn)上看不是很明顯:

            
>>> for x in weave('abc', xrange(4), [10,11,12,13,14]):
...  print x,
...
a 0 10 b 1 11 c 2 12 13 3 14


          

即使一些迭代器到達終點,但其余迭代器會繼續(xù)產(chǎn)生值,直到在某一時刻產(chǎn)生了所有可用的值為止。

我將另外只提出一個可行的 itertools 函數(shù)。提出這個函數(shù)主要是受到了構(gòu)思問題的函數(shù)編程方法的啟發(fā)。 icompose() 與上面提出的函數(shù) ireduce() 存在某種對稱。但是在 ireduce() 將值的(惰性)序列傳遞給某個函數(shù)并產(chǎn)生每個結(jié)果的地方, icompose() 將函數(shù)序列應用于每個前趨函數(shù)的返回值。可以把 ireduce() 用于將事件序列傳遞給長期活動的對象。而 icompose() 可能將對象重復地傳遞給返回新對象的賦值函數(shù)。第一種方法是相當傳統(tǒng)的考慮事件的 OOP 方法,而第二種的思路更接近于 FP。

以下是可能的 icompose() 實現(xiàn):
清單 5. icompose() 的樣本實現(xiàn)

            
def icompose(functions, initval):
  currval = initval
  for f in functions:
    currval = f(currval)
    yield currval


          

結(jié)束語

迭代器 - 被認為是惰性序列 - 是功能強大的概念,它開啟了 Python 編程的新樣式。但是在只把迭代器當作數(shù)據(jù)源與把它作為一種序列來考慮之間存在著微妙的差別。這兩種想法本質(zhì)上哪一種都不見得比另一種更正確,但是后者開創(chuàng)了操作編程事件的一種組合性的簡略表達方法。 itertools 中的組合函數(shù)(尤其是它可能產(chǎn)生的一些類似于我建議的函數(shù))接近于編程的聲明樣式。對我而言,這些聲明樣式一般都更不易出錯且更強大。


更多文章、技術(shù)交流、商務合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

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

【本文對您有幫助就好】

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

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 玛沁县| 漯河市| 太白县| 芜湖县| 柳江县| 涟源市| 盐津县| 松江区| 林口县| 璧山县| 姜堰市| 竹北市| 普兰县| 全州县| 大同市| 桂林市| 秀山| 云南省| 双鸭山市| 饶平县| 中西区| 锡林浩特市| 宁德市| 德阳市| 沧源| 克什克腾旗| 龙南县| 黄冈市| 资源县| 德钦县| 安西县| 孙吴县| 潞城市| 尼勒克县| 乌兰察布市| 阿克苏市| 平武县| 永吉县| 刚察县| 虎林市| 迭部县|