4

この質問では @ lazyrは、ここizip_longestからのイテレータの次のコードがどのように機能するかを尋ねます。

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送信されるイテレーターの1つで発生した場合はどうなるか」という質問に遭遇しました。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_longesしたがって、 fromのコードは例外をitertools伝播しましたがIndexError(私が思うに)、反復を停止するためのシグナルとして例外をizip_longes_from_docs「飲み込んだ」ことがはっきりとわかります。IndexErrorsentinel

私の質問は、モジュールIndexError内のコードでの伝播をどのように回避したかということです。itertools

4

1 に答える 1

3

コードでは、センチネルは使用されていませんizip_longest_nextizip_longest

代わりに、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
于 2011-09-12T20:18:31.473 に答える