复现 bug

定义 print_iterator

In [1]:
def print_iterator(iterator):
    while True:
        try:
            element = next(iterator)
        except StopIteration:
            break
        else:
            print(element)

测试 print_iterator

In [3]:
print_iterator(iter([1, 2, 3]))
1
2
3

工作完全正常

counter_iterable

定义 counter_iterable

In [7]:
def counter_iterable(iterable):
    iterator = iter(iterable)
    iterator_length = sum(1 for _ in iterator)

    print(iterator_length)

测试 counter_iterable

In [6]:
counter_iterable([1, 2, 3])
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-6-34aa9b80fd4b> in <module>()
----> 1 counter_iterable([1, 2, 3])

NameError: name 'counter_iterable' is not defined

工作完全正常

组合在一起

定义 counter_iterable_and_print

In [9]:
def counter_iterable_and_print(iterable):
    iterator = iter(iterable)
    iterator_length = sum(1 for _ in iterator)

    print(iterator_length)

    print_iterator(iterator)

测试 counter_iterable_and_print

In [10]:
counter_iterable_and_print([1, 2, 3])
3

工作不正常!

原因

iterator 内有有状态信息,具有不可重入(non-reentrant)的特性,这个和 list, tuple, dict 等容器不一样,容器通过 __getitem__ 来迭代。

示例代码:

In [12]:
iterable = [1, 2, 3]
iterator = iter(iterable)
iterator_length = sum(1 for _ in iterator)
iterator_length_2 = sum(1 for _ in iterator)
In [13]:
print(iterator_length)
print(iterator_length_2)
3
0

解决方案

每次循环都重新生成一个 iterator 对象

In [14]:
iterable = [1, 2, 3]

iterator = iter(iterable)
iterator_length = sum(1 for _ in iterable)

iterator = iter(iterable)
iterator_length_2 = sum(1 for _ in iterable)
In [15]:
print(iterator_length)
print(iterator_length_2)
3
3