Python中“可迭代”究竟是什么意思?

时间:2015-09-26 17:32:03

标签: python iterable

首先我要澄清,我不是在问什么是“iterator”。

这就是Python的doc

中定义术语“可迭代”的方式
  

迭代
  一个能够一次返回其成员的对象。   迭代的例子包括所有序列类型(例如list,str,   和元组)和一些非序列类型,如dict,文件对象和   您使用 __ iter __()或__getitem __()定义的任何类的对象   方法。 Iterables可用于for循环和许多其他地方   需要序列的地方(zip(),map(),...)。当一个可迭代的   object作为参数传递给内置函数iter(),它   返回对象的迭代器。这个迭代器适用于一次通过   超过这组价值观。使用iterables时,通常不会   必须调用iter()或自己处理迭代器对象。该   for语句为您自动执行,创建临时   用于在循环期间保存迭代器的未命名变量。   另请参见迭代器,序列和生成器。

作为other people suggested,使用isinstance(e, collections.Iterable)是检查对象是否可迭代的最pythonic方式。
所以我用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'

现在它被认为是一个“真正的”可迭代对象,尽管它在迭代时不能产生任何东西。

我是否误解了某些内容或文档不正确?

3 个答案:

答案 0 :(得分:2)

我认为这里的混淆点是,虽然实现__getitem__ 允许你迭代一个对象,但它不是定义的接口的一部分按Iterable

abstract base classes允许一种形式的虚拟子类化,其中实现指定方法的类(在Iterable的情况下,只有__iter__)被isinstance考虑并且issubclass是ABCs 的子类,即使它们没有明确地继承它们。但是,它不会检查方法实现是否真正起作用,只是它是否提供。

有关详细信息,请参阅PEP-3119,其中介绍了ABCs。

  

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

我不同意;我会使用duck-typing而只是尝试迭代对象。如果对象不可迭代,则会引发TypeError,如果您想处理不可迭代的输入,则可以在函数中捕获,或者如果不想,则允许覆盖到调用者。这完全反过来决定了对象如何决定实现迭代,并且只是在最合适的时间找出它是否存在。

要添加更多内容,我认为您引用的文档略微误导。引用iter docs,这可能会清除这一点:

  

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

这清楚地表明,虽然两个协议都使对象可迭代,但只有一个是实际的"迭代协议" ,正是isinstance(thing, Iterable)测试对于。因此,我们可以得出结论,在最常见的情况下,检查"您可以迭代的事情的一种方法是"

isinstance(thing, (Iterable, Sequence))

虽然这也要求您实施__len__以及__getitem__"虚拟子类" Sequence

答案 1 :(得分:0)

是可迭代的。但是,你还没有从abc.Iterable继承,所以Python自然不会将它报告为来自该类的后代。

这两个东西 - 可迭代的,从该基类下降 - 是完全独立的。

答案 2 :(得分:-1)

Iterable是(收集任何东西)的东西,允许对其元素进行某种迭代。但是python中迭代的通用方法是什么?这是使用 - in关键字,它使用对象的__iter__方法。因此,在该术语中,定义__iter__的任何对象都可以与in一起使用,并且是可迭代的。

所以,大多数'鸭子式'检查一个对象是否可迭代的方法是,如果一个对象是这样的,(是的,我知道隐含的是isinstance情况下发生的事情,因为虚拟类)

hasattr(train, '__iter__')

因为根据鸭子打字,我们关心的是一个物体而不是它的祖先所提供的行为。

如果你有__iter__的错误实现并不意味着对象不可迭代,那只意味着你的代码中有错误。

注意: - 那些没有定义__iter__的对象在一般意义上仍然可以迭代,通过使用其他方法,只是它们不能与in关键字一起使用。 例如:NumberList实例可以在each方法上迭代,但在python意义上不可迭代。

class NumberList:

     def __init__(self, values):
         self.values = values

     def each(self):
         return self.values