“可迭代"到底是什么?在Python中意味着什么?为什么我的实现__getitem __()的对象不是可迭代的? [英] What exactly does "iterable" mean in Python? Why isn't my object which implements `__getitem__()` an iterable?

查看:138
本文介绍了“可迭代"到底是什么?在Python中意味着什么?为什么我的实现__getitem __()的对象不是可迭代的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,我想澄清一下,我不是在问什么是迭代器".

First I want to clarify, I'm NOT asking what is "iterator".

这是在Python的 doc :

This is how the term "iterable" is defined in Python's doc:

可迭代

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

An object capable of returning its members one at a time. Examples of iterables include all sequence types (such as list, str, and tuple) and some non-sequence types like dict, file objects, and objects of any classes you define with an __iter__() or __getitem__() method.

Iterables可以在for循环和许多其他地方使用 需要序列的位置(zip(),map()等).当一个可迭代的 对象作为参数传递给内置函数iter(),它 返回对象的迭代器.此迭代器适合一次通过 超过一组值.使用可迭代对象时,通常不 自己调用iter()或处理迭代器对象所必需.这 for语句会自动为您创建一个临时语句 在循环期间保存迭代器的未命名变量.

Iterables can be used in a for loop and in many other places where a sequence is needed (zip(), map(), ...). When an iterable object is passed as an argument to the built-in function iter(), it returns an iterator for the object. This iterator is good for one pass over the set of values. When using iterables, it is usually not necessary to call iter() or deal with iterator objects yourself. The for statement does that automatically for you, creating a temporary unnamed variable to hold the iterator for the duration of the loop.

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

See also iterator, sequence, and generator.

作为其他人建议,使用isinstance(e, collections.Iterable)是检查对象是否可迭代的最有效的方法.
所以我用Python 3.4.3做了一些测试:

As other people suggested, using isinstance(e, collections.Iterable) is the most pythonic way to check if an object is iterable.
So I did some test with 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__方法,但是它不被视为可迭代的对象,更不用说它能够一次返回一个数字了.

The result is quite strange: MyTrain has defined __getitem__ method, but it is not considered as an iterable object, not to mention it's capable of returning one number at a time.

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

Then I removed __getitem__ and added the __iter__ method:

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'

尽管它在迭代时无法产生任何东西,但现在被认为是真正的"可迭代对象.

It is now considered as a "true" iterable object in spite of it cannot produce anything while iterating.

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

So did I misunderstand something or is the documentation incorrect?

推荐答案

我认为这里的困惑点在于,尽管实现__getitem__ 确实允许您迭代对象,但不是 .

I think the point of confusion here is that, although implementing __getitem__ does allow you to iterate over an object, it isn't part of the interface defined by Iterable.

抽象基类允许以下形式虚拟子类,其中实现特定方法的类(在Iterable中,只有__iter__)被isinstanceissubclass视为ABC 的子类,即使它们未显式从他们那里继承.但是,它不会检查方法实现是否确实有效,而只是检查是否提供了方法.

The abstract base classes allow a form of virtual subclassing, where classes that implement the specified methods (in the case of Iterable, only __iter__) are considered by isinstance and issubclass to be subclasses of the ABCs even if they don't explicitly inherit from them. It doesn't check whether the method implementation actually works, though, just whether or not it's provided.

有关更多信息,请参见 PEP-3119 ,其中介绍了ABC.

For more information, see PEP-3119, which introduced ABCs.

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

using isinstance(e, collections.Iterable) is the most pythonic way to check if an object is iterable

我不同意;我会使用鸭子型,然后尝试遍历对象.如果对象不是可迭代的,则会引发TypeError,如果您要处理不可迭代的输入,则可以捕获到函数中;如果不可以,则可以渗透到调用者中.这完全避开了对象决定执行迭代的方式,只是找出它是否在最合适的时间进行.

I disagree; I would use duck-typing and just attempt to iterate over the object. If the object isn't iterable a TypeError will be raised, which you can catch in your function if you want to deal with non-iterable inputs, or allow to percolate up to the caller if not. This completely side-steps how the object has decided to implement iteration, and just finds out whether or not it does at the most appropriate time.

要添加更多内容,我认为您引用的文档略有误导性.引用 iter文档,也许可以解决此问题:

To add a little more, I think the docs you've quoted are slightly misleading. To quote the iter docs, which perhaps clear this up:

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

object must be a collection object which supports the iteration protocol (the __iter__() method), or it must support the sequence protocol (the __getitem__() method with integer arguments starting at 0).

这清楚地表明,尽管两种协议都使对象可迭代,但只有一个是实际的迭代协议" ,而isinstance(thing, Iterable)正是为此进行测试的.因此,我们可以得出结论,在最一般的情况下,一种检查您可以迭代的内容" 的方法是:

This makes it clear that, although both protocols make the object iterable, only one is the actual "iteration protocol", and it is this that isinstance(thing, Iterable) tests for. Therefore we could conclude that one way to check for "things you can iterate over" in the most general case would be:

isinstance(thing, (Iterable, Sequence))

尽管这确实还需要您将__len____getitem__一起实现为虚拟子类" Sequence.

although this does also require you to implement __len__ along with __getitem__ to "virtually sub-class" Sequence.

这篇关于“可迭代"到底是什么?在Python中意味着什么?为什么我的实现__getitem __()的对象不是可迭代的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆