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

How come an object that implements __iter__ is not recognized as iterable?

假设您使用包装器对象:

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

我不明白为什么。

基本上是因为您的 class 没有真正的 __iter__ 方法:

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

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

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

Note

This method may still be bypassed when looking up special methods as the result of implicit invocation via language syntax or built-in functions. See Special method lookup.

后面的部分也解释了为什么:

Bypassing the __getattribute__() machinery in this fashion provides significant scope for speed optimisations within the interpreter, at the cost of some flexibility in the handling of special methods (the special method must be set on the class object itself in order to be consistently invoked by the interpreter).

强调我的。