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

发布于 2021-01-29 16:56:20

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

这就是在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'

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

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

关注者
0
被浏览
154
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

    我认为此处的困惑点在于,尽管实现__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



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看