2011-09-12 53 views
4

this問題@lazyr詢問如何izip_longest迭代從here作品下面的代碼:itertools中的izip_longest:迭代器中的IndexError如何工作?

def izip_longest_from_docs(*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 

當我試圖理解它是如何工作的我偶然到一個問題: 「如果IndexError是內部人提出那些被髮送到izip_longest作爲參數的迭代器?「。

然後我寫了一些測試代碼:

from itertools import izip_longest, repeat, chain, izip 

def izip_longest_from_docs(*args, **kwds): 
    # The code is exactly the same as shown above 
    .... 

def gen1(): 
    for i in range(5): 
     yield i 

def gen2(): 
    for i in range(10): 
     if i==8: 
      raise IndexError #simulation IndexError raised inside the iterator 
     yield i 

for i in izip_longest_from_docs(gen1(),gen2(), fillvalue = '-'): 
    print('{i[0]} {i[1]}'.format(**locals())) 

print('\n') 

for i in izip_longest(gen1(),gen2(), fillvalue = '-'): 
    print('{i[0]} {i[1]}'.format(**locals())) 

而事實證明,在itertools模塊和izip_longest_from_docs工作中的作用是不同的。

以上代碼的輸出:

>>> 
0 0 
1 1 
2 2 
3 3 
4 4 
- 5 
- 6 
- 7 


0 0 
1 1 
2 2 
3 3 
4 4 
- 5 
- 6 
- 7 

Traceback (most recent call last): 
    File "C:/..., line 31, in <module> 
    for i in izip_longest(gen1(),gen2(), fillvalue = '-'): 
    File "C:/... test_IndexError_inside iterator.py", line 23, in gen2 
    raise IndexError 
IndexError 

因此,它清楚地看到,那的izip_longesitertools代碼並傳播IndexError異常(因爲我認爲它應該),但izip_longes_from_docs「吞噬」 IndexError例外因爲它將sentinel作爲信號停止迭代。

我的問題是,他們是如何在itertools模塊的代碼中傳播IndexError

+0

@agf我想要的,但我找不到它。感謝您的鏈接!我會看看。 – ovgolovin

+0

@agf它似乎在C:o)對我來說太複雜了。儘管如此,據稱是**的等價物在功能(而不是速度)方面似乎不是絕對等價的。 – ovgolovin

+0

是的,你是對的。 「如果您在本文檔中發現錯誤或想要提出改進建議,請發送電子郵件至[email protected]描述錯誤以及您在何處找到錯誤。如果您有建議如何解決該錯誤,請包括以及「。如果你不喜歡它,我會去做嗎? – agf

回答

3

in izip_longest_next in code of izip_longest,no sentinel is used。

取而代之的是,CPython會跟蹤有多少迭代器仍然處於活動狀態,並在活動數達到零時停止。

如果發生錯誤,它會結束迭代,就好像沒有迭代器仍處於活動狀態,並允許錯誤傳播。

代碼:

  item = PyIter_Next(it); 
      if (item == NULL) { 
       lz->numactive -= 1; 
       if (lz->numactive == 0 || PyErr_Occurred()) { 
        lz->numactive = 0; 
        Py_DECREF(result); 
        return NULL; 
       } else { 
        Py_INCREF(lz->fillvalue); 
        item = lz->fillvalue; 
        PyTuple_SET_ITEM(lz->ittuple, i, NULL); 
        Py_DECREF(it); 
       } 
      } 

我看到的最簡單的解決方案:

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

謝謝! 不過,我必須補充一點,在我看來,所謂的**純** Python **等效**與功能本身略有不同。奇怪的是,他們爲什麼以這種方式來表達這種等值。無論如何,要了解帶有'sentinel'的代碼對於大腦來說是一件好事。 – ovgolovin

+0

我測試過了。它不會像它應該那樣工作。 – ovgolovin

+0

@ovgo再試一次嗎?我第一次發佈後編輯它。它似乎爲我工作。 – agf