python itertools循环解释

xua*_*yue 6 python iterator

我在https://more-itertools.readthedocs.io/en/latest/api.html 中遇到了这个循环的代码片段,但我无法理解最后一行如何nexts = cycle(islice(nexts, pending))从 nexts 迭代器中删除耗尽的生成器。据我了解,这从输入中删除了最后一个,但我们不应该删除第一个,即当前抛出此异常的那个吗?

def roundrobin(*iterables):
    """Yields an item from each iterable, alternating between them.

        >>> list(roundrobin('ABC', 'D', 'EF'))
        ['A', 'D', 'E', 'B', 'F', 'C']

    This function produces the same output as :func:`interleave_longest`, but
    may perform better for some inputs (in particular when the number of
    iterables is small).

    """
    # Recipe credited to George Sakkis
    pending = len(iterables)
    if PY2:
        nexts = cycle(iter(it).next for it in iterables)
    else:
        nexts = cycle(iter(it).__next__ for it in iterables)
    while pending:
        try:
            for next in nexts:
                yield next()
        except StopIteration:
            pending -= 1
            nexts = cycle(islice(nexts, pending))
Run Code Online (Sandbox Code Playgroud)

abc*_*ccd 5

我会一步一步地为你分解:

>>> list(roundrobin('ABC', 'D', 'EF'))


pending = 3  # len(('ABC', 'D', 'EF'))

# nexts roughly equivalent to:
['ABC', 'D', 'EF', 'ABC', 'D', 'EF', 'ABC', ...]

***

# the following get yielded 
'A' from 'ABC' # nexts: ['D' , 'EF', 'BC', 'D' , 'EF', ...]
'D' from 'D'   # nexts: ['EF', 'BC', ''  , 'EF', 'BC', ...]
'E' from 'EF'  # nexts: ['BC', ''  , 'F' , 'BC', ''  , ...]
'B' from 'BC'  # nexts: [''  , 'F' , 'C' , ''  , 'F' , ...]

# StopIteration was raised by what used to be "B"
SI from ''  # nexts:  ['F', 'C', '', 'F', 'C', '', 'F', ...]
#                                 ^ index 2 (`pending`[see the following line])

# pending -= 1
pending = 2

# when islice() is used with one argument, it defaults to the "stop" index
# so islice() returns ['F', 'C']
# and cycle() converts it to ['F', 'C', 'F', 'C', ...]

pending = 2
nexts: ['F', 'C', 'F', 'C', ...]

# Go back to *** and continue until pending = 0
Run Code Online (Sandbox Code Playgroud)

这就是最后一行删除耗尽的可迭代对象的方式。


大意:

for next in nexts:
    yield next()
Run Code Online (Sandbox Code Playgroud)

即使StopIteration被引发,耗尽的迭代已经被使用了nexts

因此,不要将耗尽的迭代保留为中的第一项nexts

nexts = ['', 'F', 'C', '', 'F', 'C', '', 'F', ...]
Run Code Online (Sandbox Code Playgroud)

第一项nexts实际上是下一项:

nexts = ['F', 'C', '', 'F', 'C', '', 'F', ...]
Run Code Online (Sandbox Code Playgroud)