“可迭代”在Python中到底是什么意思?为什么我的实现__getitem __的对象不是可迭代的?

问题描述

我认为此处的困惑点在于,尽管实现__getitem__ 确实 允许您迭代对象,但它不是定义的接口的一部分Iterable

抽象基类允许虚拟的子类,其中实现了指定的方法(在的情况下,类的形式Iterable,只__iter__),被认为是由isinstanceissubclass是的ABC的子类,即使他们没有明确地从他们继承 。但是,它不会检查方法实现是否 真正有效 ,而只是检查是否提供了方法实现。

有关更多信息,请参阅介绍ABC的PEP-3119

使用isinstance(e, collections.Iterable)是检查对象是否可迭代的最pythonic方法

我不同意; 我会用鸭式打字,只是 。如果对象不是可迭代的,TypeError则将引发,如果您要处理不可迭代的输入,则可以捕获函数;如果不可以,则允许渗透到调用者。这完全避开了对象决定执行迭代的方式,只是找出它是否在最合适的时间执行。

要补充一点,我认为您引用的文档 有些 误导。引用iterdocs,也许可以解决这个问题:

object 必须是支持迭代协议(该__iter__()方法)的集合对象,或者它必须支持序列协议(以__getitem__()开头的整数参数的方法0)。

这清楚地表明,尽管两种协议都使对象可迭代,但是只有一个是实际的 “迭代协议” ,并且正是要isinstance(thing, Iterable)测试的对象。因此,我们可以得出结论,在最一般的情况下,一种检查 “可以迭代的事物”方法是:

isinstance(thing, (Iterable, Sequence))

尽管这还需要你来实现__len__沿__getitem__“虚拟子类” Sequence

解决方法

首先,我想澄清一下,我不是在问什么是“迭代器”。

这就是在Python的doc中定义“可迭代”一词的方式:

可重复的

一个能够一次返回其成员的对象。
可迭代的示例包括所有序列类型(例如list,str和tuple)以及一些非序列类型,例如dict,文件对象以及您使用 iter
()或__getitem __()
方法定义的任何类的对象。

Iterables可用于for循环以及需要序列的许多其他地方(zip(),map()等)。将可迭代对象作为参数传递给内置函数iter()时,它将返回该对象的迭代器。此迭代器非常适合在一组值上传递。使用Iterables时,通常不必自己调用iter()或处理迭代器对象。for语句会自动为您执行此操作,并创建一个临时的未命名变量来在迭代过程中保留迭代器。

另请参见迭代器,序列和生成器。

正如其他人所建议的那样,usingisinstance(e,collections.Iterable)是检查对象是否可迭代的最Python方式。
所以我用Python 3.4.3做了一些测试:

from collections.abc import Iterable

class MyTrain:
    def __getitem__(self,index):
        if index > 3:
            raise IndexError("that's enough!")

        return index

for name in MyTrain():
    print(name)  # 0,1,2,3

print(isinstance(MyTrain(),Iterable))  # False

结果很奇怪:MyTrain已经定义了__getitem__方法,但是它不被视为可迭代的对象,更不用说它能够一次返回一个数字。

然后我删除__getitem__并添加了__iter__方法:

from collections.abc import Iterable

class MyTrain:    
    def __iter__(self):
        print("__iter__ called")
        pass

print(isinstance(MyTrain(),Iterable))  # True

for name in MyTrain():
    print(name)  # TypeError: iter() returned non-iterator of type 'NoneType'

尽管它在迭代时无法产生任何东西,但现在将其视为“真实”可迭代对象。

那我误会了什么还是文档不正确?