取消注释 `if False: yield` 改变了 `__iter__` 行为

问题描述

当我取消注释 __iter__ 时,我不明白为什么 if False: yield 的行为会发生变化。条件永远不为真,那么为什么结果会改变?

class Example:
    def __init__(self):
        self.lst = [1,2,3]
        self.size = 3

    def __iter__(self):
        self.n = 0
        #if False: yield 
        return self

    def __next__(self):
        self.n += 1
        if self.n > self.size:
            raise stopiteration
        return self.lst[self.n-1]

ex = Example()
for i in ex:
    print(i) 

在这代码中,一切都按预期工作,并打印出列表。

如果我取消对 if False: yield 方法中的 __iter__ 的注释,则迭代器将停止工作并且不会打印任何内容,即使该行从未被执行。

解决方法

当您取消注释行 if False: yield 时,Python 解释器会将 def __iter__(self) 编译为 generator 而不是函数。生成器是封装在函数中的迭代器,只要 next 语句继续提供值,它就会一直返回值(在 yield 调用上),并且在生成器返回时会引发 StopIteration其功能:

>>> class A:
...  def __iter__(self):
...    if False:
...      yield
...
>>> a = A()
>>> type(iter(a))
<class 'generator'>
>>> class B:
...   def __iter__(self):
...     return self
...   def __next__(self):
...     return 1
...
>>> type(iter(B()))
<class '__main__.B'>

当您注释该行时,您的 __iter__ 是一个常规函数并返回 self 作为迭代器,任何实现 __next__ 方法的类都可以用作迭代器;如果你没有你会得到这个错误:

>>> class B:
...   def __iter__(self):
...     return self
...
>>> type(iter(B()))
Traceback (most recent call last):
  File "<stdin>",line 1,in <module>
TypeError: iter() returned non-iterator of type 'B'

请注意,如果 __iter__ 所做的只是返回 self 而您甚至不需要实现它,那么只需 __next__ 就足够了。您的 __iter__ 实现甚至是危险的:多个 iter(ex) 调用都将返回与迭代器相同的 ex 对象,并将重置所有这些“迭代器”的 n 实际上使用相同的ex

>>> ex = Example()
>>> itr1 = iter(ex)
>>> print(next(itr1),next(itr1))
1 2
>>> iter(ex)
>>> print(next(itr1))
1

再次调用 iter 会重置您之前的迭代器引用吗?我无法想象这就是您想要实现的目标 ;)

,

如果条件为 if,则 False 循环永远不会执行。因此,当您使用 if False: 时,因为条件是 False,它永远不会执行。此外,您没有在 yield 中指定任何内容。

这可以改写为,

class Example:
    def __init__(self):
        self.lst = [1,2,3]
        self.size = 3
        self.n = 0

    def __iter__(self):
        self.n = 0
        for i in self.lst:
            yield i

    def __next__(self):
        self.n += 1
        if self.n > self.size:
            raise StopIteration
        return self.lst[self.n-1]

ex = Example()
for i in ex:
    print(i)

输出将如您所愿。