为什么实现 __iter__ 的对象不被识别为可迭代的?

use*_*082 5 python iterable python-3.x

假设您使用包装器对象:

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)
Run Code Online (Sandbox Code Playgroud)

此对象实现__iter__,因为它将对它的任何调用传递给实现它的成员f。案例:

>>> x = IterOrNotIter()
>>> x.__iter__().__next__()
'Whatever was in /tmp/toto.txt\n'
Run Code Online (Sandbox Code Playgroud)

根据文档(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
Run Code Online (Sandbox Code Playgroud)

而这有效:

>>> x = IterOrNotIter()
>>> for l in x.f:
...    print(l)
...
Whatever was in /tmp/toto.txt
Run Code Online (Sandbox Code Playgroud)

我不明白为什么。

MSe*_*ert 4

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

>>> hasattr(IterOrNotIter, '__iter__')
False
Run Code Online (Sandbox Code Playgroud)

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

这实际上在文档中提到过__getattribute__

笔记

当通过语言语法或内置函数查找作为隐式调用结果的特殊方法时,仍然可能会绕过此方法。请参阅特殊方法查找

后一节还解释了原因

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

强调我的。

  • 嗯,这真是令人失望。解释器检查而不是仅仅调用这一事实感觉不符合Python标准。 (2认同)