问题描述
我认为此处的困惑点在于,尽管实现__getitem__
确实 允许您迭代对象,但它不是定义的接口的一部分Iterable
。
该抽象基类允许虚拟的子类,其中实现了指定的方法(在的情况下,类的形式Iterable
,只__iter__
),被认为是由isinstance
和issubclass
是的ABC的子类,即使他们没有明确地从他们继承 。但是,它不会检查方法实现是否 真正有效 ,而只是检查是否提供了方法实现。
有关更多信息,请参阅介绍ABC的PEP-3119。
使用
isinstance(e, collections.Iterable)
是检查对象是否可迭代的最pythonic方法
我不同意; 我会用鸭式打字,只是
。如果对象不是可迭代的,TypeError
则将引发,如果您要处理不可迭代的输入,则可以捕获函数;如果不可以,则允许渗透到调用者。这完全避开了对象决定执行迭代的方式,只是找出它是否在最合适的时间执行。
要补充一点,我认为您引用的文档 有些
误导。引用iter
docs,也许可以解决这个问题:
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'
尽管它在迭代时无法产生任何东西,但现在将其视为“真实”可迭代对象。
那我误会了什么还是文档不正确?