为什么一些(但不是全部)Python 迭代器在用完后可以求和?

Why are some (but not all) Python iterators summable after being exhausted?

在 Python 中,迭代器旨在一次性使用。一旦迭代器引发了 StopIteration,它就不应该 return 任何更多值。然而,如果我定义一个自定义迭代器,似乎我仍然可以在它们耗尽后对值进行求和!

示例代码(Python 3.6.5,或将 __next__(self) 替换为 next(self) 以查看 Python 2.7.15 中的相同行为:

class CustomIterator:
  def __iter__(self):
    self.n=0
    return self

  def __next__(self):
    self.n += 1
    if self.n > 3:
      raise StopIteration
    return self.n

i1 = iter([1,2,3])
i2 = iter(CustomIterator())

print('Sum of i1 is {}'.format(sum(i1))) # returns 6 as expected
print('Sum of i1 is {}'.format(sum(i1))) # returns 0 because i1 is now exhausted
try:
  print(next(i1))
except StopIteration:
  print("i1 has raised StopIteration") # this exception happens
print('Sum of i1 is {}'.format(sum(i1))) # 0 again

print('Sum of i2 is {}'.format(sum(i2))) # returns 6 as expected
print('Sum of i2 is {}'.format(sum(i2))) # returns 6 again!
try:
  print(next(i2))
except StopIteration:
  print("i2 has raised StopIteration") # still get an exception
print('Sum of i2 is {}'.format(sum(i2))) # and yet we get 6 again

为什么 i1 和 i2 的行为不同? sum 是如何实现的一些技巧?我已经检查过 https://docs.python.org/3/library/functions.html#sum,它并没有给我很多继续下去的机会。

相关问题:

这些描述了内置迭代器的预期行为,但没有解释为什么我的自定义迭代器的行为不同。

问题是自定义迭代器正在 __iter__ 方法内部初始化。即使 i2 = iter(CustomIterator()) 包含对 iter 的显式调用,sum 函数(以及 minmaxfor 等)仍将再次调用 i2.__iter__() 并重置 i2.

有很多关于“如何制作 Python 迭代器”的教程,其中大约一半说“要制作迭代器,你只需要定义 iternext 方法”。虽然根据 the documentation 这在技术上是正确的,但有时会给您带来麻烦。在许多情况下,您还需要一个单独的 __init__ 方法来初始化迭代器。

因此,要解决此问题,请将 CustomIterator 重新定义为:

class CustomIterator:
  def __init__(self):
    self.n=0

  def __iter__(self):
    return self

  def __next__(self):
    self.n += 1
    if self.n > 3:
      raise StopIteration
    return self.n

i1 = iter([1,2,3])
i2 = CustomIterator() ### iter(...) is not needed here (but won't do any harm either)

然后 init 只在创建新迭代器时调用一次,重复调用 iter 不会重置迭代器。