【问题标题】:Uncommenting `if False: yield` changes `__iter__` behaviour取消注释 `if False: yield` 会改变 `__iter__` 行为
【发布时间】:2021-04-10 18:12:49
【问题描述】:

我不明白为什么当我取消注释 if False: yield__iter__ 的行为会发生变化。条件永远不成立,为什么结果会发生变化?

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) 

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

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

【问题讨论】:

  • 你发现的是 Python 中 Iterator 和 Iterable 对象之间的细微差别

标签: python iterator generator


【解决方案1】:

当您取消注释 if False: yield 行时,Python 解释器会将 def __iter__(self) 编译为 generator 而不是函数。生成器是一个封装在函数中的迭代器,只要yield 语句继续提供值,它将保持返回值(在next 调用上),并且当生成器返回其函数时将引发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 会重置您之前的迭代器引用?我无法想象这就是你想要达到的目标;)

【讨论】:

    【解决方案2】:

    如果条件为False,则永远不会执行if 循环。因此,当您使用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)
    

    输出将根据需要。

    【讨论】:

    • 嗨 maney,我的问题与 if False 行会破坏代码这一事实有关,即使它从未运行过。你知道为什么吗?
    • 当指定yield 时,该函数将转换为生成器。所以,它现在是一个生成器,但它不会产生任何东西,因为 if 循环是 False
    猜你喜欢
    • 2013-03-11
    • 2015-09-19
    • 1970-01-01
    • 2018-02-03
    • 2014-05-07
    • 2015-12-19
    • 2014-04-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多