【问题标题】:How come an object that implements __iter__ is not recognized as iterable?为什么实现 __iter__ 的对象不被识别为可迭代的?
【发布时间】:2017-10-11 14:57:42
【问题描述】:

假设您使用包装对象:

class IterOrNotIter:
    def __init__(self):
        self.f = open('/tmp/toto.txt')
    def __getattr__(self, item):
        try:
            return self.__getattribute__(item)
        except AttributeError:
            return self.f.__getattribute__(item)

这个对象实现了__iter__,因为它将对它的任何调用传递给实现它的成员f。举个例子:

>>> x = IterOrNotIter()
>>> x.__iter__().__next__()
'Whatever was in /tmp/toto.txt\n'

根据文档 (https://docs.python.org/3/library/stdtypes.html#iterator-types),IterOrNotIter 因此应该是可迭代的。

但是,Python 解释器无法将 IterOrNotIter 对象识别为实际上是可迭代的:

>>> x = IterOrNotIter()
>>> for l in x:
...    print(l)
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'IterOrNotIter' object is not iterable

虽然这有效:

>>> x = IterOrNotIter()
>>> for l in x.f:
...    print(l)
...
Whatever was in /tmp/toto.txt

我不明白为什么。

【问题讨论】:

    标签: python python-3.x iterable


    【解决方案1】:

    基本上是因为你的类没有真正的__iter__ 方法:

    >>> hasattr(IterOrNotIter, '__iter__')
    False
    

    所以它不符合迭代器的条件,因为对__iter__ 的实际检查是检查是否存在而不是假设它已实现。因此,__getattr____getattribute__(不幸的是)的解决方法不起作用。

    这实际上在__getattribute__的文档中提到:

    注意

    在通过语言语法或内置函数隐式调用查找特殊方法时,仍可能绕过此方法。见Special method lookup

    后一部分还解释了为什么

    以这种方式绕过__getattribute__() 机制为解释器中的速度优化提供了很大的空间,但代价是处理特殊方法的一些灵活性(必须在类对象本身上设置特殊方法)为了被解释器一致地调用)。

    强调我的。

    【讨论】:

    • 嗯,这令人失望。解释器检查而不是仅仅调用的事实让人感觉很奇怪。
    • @user589082 是的,这有点不符合 Python 标准,但根据 special methods 的文档(我更新了答案),这使得 Python 速度显着加快 - 以牺牲灵活性为代价。
    • @MSeifert: 好的,但是文档应该明确警告,尝试对不可迭代对象上的 __iter____next__ 方法进行猴子修补似乎会在修补方法时起作用,但是当我们尝试使用它们进行迭代时,修补的方法将被忽略,并且对象将引发异常并失败。否则,这是违反代码意图的不明显的“神奇”行为。
    • @smci Python 有一个问题跟踪器。您可以在那里要求对相关文档进行修订/澄清。
    猜你喜欢
    • 2018-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-22
    • 2010-11-20
    • 1970-01-01
    • 2020-04-09
    相关资源
    最近更新 更多