Python 中的“可迭代”究竟是什么意思?为什么我的实现 `__getitem__()` 的对象不是可迭代的?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32799980/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What exactly does "iterable" mean in Python? Why isn't my object which implements `__getitem__()` an iterable?
提问by laike9m
First I want to clarify, I'm NOT asking what is "iterator".
首先我想澄清一下,我不是在问什么是“迭代器”。
This is how the term "iterable" is defined in Python's doc:
这就是 Python文档中“可迭代”一词的定义方式:
iterable
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 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.
可迭代的
能够一次返回一个成员的对象。可迭代对象的示例包括所有序列类型(例如列表、字符串和元组)和一些非序列类型,例如 dict、文件对象以及您使用__iter__() 或 __getitem__()方法定义的任何类的对象。
可迭代对象可用于 for 循环和许多其他需要序列的地方(zip()、map()、...)。当可迭代对象作为参数传递给内置函数 iter() 时,它会返回该对象的迭代器。此迭代器适用于对一组值进行一次传递。使用可迭代对象时,通常不需要调用 iter() 或自己处理迭代器对象。for 语句会自动为您执行此操作,创建一个临时未命名变量以在循环期间保存迭代器。
另请参见迭代器、序列和生成器。
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:
正如其他人所建议的那样,使用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
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.
结果很奇怪:MyTrain
定义了__getitem__
方法,但它不被视为可迭代对象,更不用说它能够一次返回一个数字。
Then I removed __getitem__
and added the __iter__
method:
然后我删除__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'
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?
那么我是误解了什么还是文档不正确?
采纳答案by jonrsharpe
I think the point of confusion here is that, although implementing __getitem__
doesallow you to iterate over an object, it isn't part of the interface defined by Iterable
.
我认为这里的混淆点在于,尽管实现__getitem__
确实允许您迭代对象,但它不是Iterable
.
The abstract base classesallow 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.
该抽象基类允许虚拟的子类,其中实现了指定的方法(在的情况下,类的形式Iterable
,只__iter__
),被认为是由isinstance
和issubclass
是的ABC的子类,即使他们没有明确地从他们继承。但是,它不检查方法实现是否实际有效,只检查是否提供了它。
For more information, see PEP-3119, which introduced ABCs.
有关更多信息,请参阅PEP-3119,其中介绍了 ABC。
using
isinstance(e, collections.Iterable)
is the most pythonic way to check if an object is iterable
using
isinstance(e, collections.Iterable)
是检查对象是否可迭代的最 Pythonic 的方法
I disagree; I would use duck-typingand 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.
我不同意; 我会使用duck-typing并尝试迭代对象。如果对象不可迭代,TypeError
则将引发,如果您想处理不可迭代的输入,您可以在您的函数中捕获它,或者如果不是,则允许渗透到调用者。这完全绕过了对象决定实现迭代的方式,而只是找出它是否在最合适的时间执行。
To add a little more, I think the docs you've quoted are slightlymisleading. To quote the iter
docs, which perhaps clear this up:
再补充一点,我认为你引用的文档有点误导。引用iter
docs,这可能会澄清这一点:
objectmust 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 at0
).
object必须是一个支持迭代协议(
__iter__()
方法)的集合对象,或者它必须支持序列协议(__getitem__()
从 开始的整数参数的方法0
)。
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)
测试。因此,我们可以得出结论,在最一般的情况下,检查“可以迭代的事物”的一种方法是:
isinstance(thing, (Iterable, Sequence))
although this does also require you to implement __len__
along with __getitem__
to "virtually sub-class"Sequence
.
尽管这还需要你来实现__len__
沿__getitem__
到“虚拟子类”Sequence
。
回答by Daniel Roseman
It isan iterable. However you haven't inherited from abc.Iterable
, so naturally Python won't report it as being descended from that class. The two things -being an iterable, and descending from that base class - are quite separate.
它是一个可迭代的。但是,您没有从 继承abc.Iterable
,因此 Python 自然不会将其报告为该类的后代。两件事——作为一个可迭代的,以及从那个基类派生的——是完全分开的。
回答by hspandher
Iterable
is something(collection anything) that allows some kind of iteration on its elements. But what is the generic way of iteration in python? That is using - in
keyword, which uses __iter__
method of an object. So, in that terms any object that defines __iter__
can be used with in
, and is an Iterable.
Iterable
是允许对其元素进行某种迭代的东西(集合任何东西)。但是python中迭代的通用方式是什么?那就是使用 -in
关键字,它使用__iter__
对象的方法。因此,在那个术语中,任何定义的对象__iter__
都可以与 一起使用in
,并且是一个可迭代对象。
So, most 'duck-typish' way to check if an object is iterable is if an object is this, (Yeah, I know implicitly that is what's happening in isinstance
case as well, due to virtual classes)
因此,检查对象是否可迭代的大多数“鸭子式”方法是对象是否是这样的(是的,我隐含地知道isinstance
,由于虚拟类,在这种情况下也会发生这种情况)
hasattr(train, '__iter__')
because according to duck-typing, we care about behavior provided by an object instead of its ancestory.
因为根据鸭子类型,我们关心对象提供的行为而不是它的祖先。
If you have a faulty implementation of __iter__
that doesn't means object is not iterable, it just means that you have bug in your code.
如果你有一个错误的实现__iter__
并不意味着对象不可迭代,它只是意味着你的代码中有错误。
Note:- Those objects that don't define __iter__
can still be iterable in general sense, by using some other method, it's just they can't be used with in
keyword.
E.g.:- NumberList
instance is iterable over each
method, but isn't iterable in python sense.
注意:- 那些没有定义的对象__iter__
在一般意义上仍然可以迭代,通过使用其他一些方法,只是它们不能与in
关键字一起使用。例如:-NumberList
实例可迭代each
方法,但在 python 意义上不可迭代。
class NumberList:
def __init__(self, values):
self.values = values
def each(self):
return self.values