2011-03-14 68 views
7

我很努力地理解下面的代碼是如何工作的。它來自http://docs.python.org/library/itertools.html#itertools.izip_longest,是izip_longest迭代器的純python等價物。我對哨兵功能特別迷惑,它是如何工作的?itertools中的izip_longest:這裏發生了什麼?

def izip_longest(*args, **kwds): 
    # izip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D- 
    fillvalue = kwds.get('fillvalue') 
    def sentinel(counter = ([fillvalue]*(len(args)-1)).pop): 
     yield counter()   # yields the fillvalue, or raises IndexError 
    fillers = repeat(fillvalue) 
    iters = [chain(it, sentinel(), fillers) for it in args] 
    try: 
     for tup in izip(*iters): 
      yield tup 
    except IndexError: 
     pass 

回答

6

好的,我們可以做到這一點。關於哨兵。表達式([fillvalue]*(len(args)-1))創建一個列表,其中包含args中每個可迭代元素的一個填充值,減1。所以,對於上面的例子['-']。然後,counter被分配給該列表的功能popsentinel本身是一個generator,它在每次迭代時從該列表中彈出一個項目。您可以對sentinel返回的每個迭代器迭代一次,它總是會產生fillvalue。由sentinel返回的所有迭代器產生的項目總數爲len(args) - 1(感謝Sven Marnach澄清這一點,我誤解了它)。

現在看看這個:

iters = [chain(it, sentinel(), fillers) for it in args] 

這就是訣竅。 iters是一個列表,其中包含args中每個迭代器的迭代器。每個迭代器執行以下操作:

  1. 遍歷所有項目從args相應的迭代。
  2. 迭代一次哨兵,產生fillvalue
  3. 重複fillvalue所有的永恆。

現在,如前所述,我們只能將所有哨兵一起迭代len(args)-1次,然後纔會拋出IndexError。這很好,因爲其中一個迭代是最長的。所以,當我們提到IndexError被提出時,這意味着我們已經完成了對args中最長迭代的迭代。

不客氣。

P.S .:我希望這是可以理解的。

+2

另外,'sentinel()'可以被調用無限次而不會引發任何異常。 – 2011-03-14 13:10:11

+0

@Sven:你說的對,我解決了。 – 2011-03-14 13:31:23

2

sentinel定義幾乎等同於

def sentinel(): 
    yield ([fillvalue] * (len(args) - 1)).pop() 

不同之處在於它獲取pop綁定方法(函數對象),爲默認參數。在函數定義時評估默認參數,因此每調用一次izip_longest而不是每次調用sentinel一次。因此,函數對象「記住」列表[fillvalue] * (len(args) - 1)而不是在每次調用中重新構造這個列表。

5

sentinel()返回迭代器產生fillvalue只出現一次的功能。的由sentinel()返回的所有迭代器產生fillvalue S中的總數限制爲n-1,其中n是傳遞給izip_longest()迭代器的數量。在此數量的fillvalue s已用盡之後,對由sentinel()返回的迭代器的進一步迭代將引發IndexError

該函數用於檢測是否所有迭代器都已耗盡:每個迭代器都是chain(),迭代器由sentinel()返回。如果所有迭代器都耗盡,則由sentinel()返回的迭代器將迭代第n次,從而產生IndexError,從而依次觸發izip_longest()的末尾。

到目前爲止,我解釋了sentinel()的作用,而不是它如何工作。當調用izip_longest()時,將評估sentinel()的定義。評估定義時,還會評估sentinel()的默認參數,每調用一次izip_longest()。該代碼相當於

fillvalue_list = [fillvalue] * (len(args)-1) 
def sentinel(): 
    yield fillvalue_list.pop() 

在默認參數,而不是在一個封閉的範圍變量存儲這僅僅是一個優化,是.pop在默認參數的包容性,因爲它節省尋找它的每迭代器返回迭代器的時間爲sentinel()

相關問題