首先我要澄清,我不是在问什么是"迭代器".
这就是Python的doc中定义"iterable"这个术语的方式:
iterable
一个能够一次返回一个成员的对象. 迭代的示例包括所有序列类型(例如list,str和tuple)和一些非序列类型,如dict,文件对象,以及使用__iter __()或__getitem __()定义的任何类的对象 方法.Iterables可用于for循环以及需要序列的许多其他地方(zip(),map(),...).当一个可迭代对象作为参数传递给内置函数iter()时,它返回该对象的迭代器.这个迭代器适用于一组值的一次传递.使用iterables时,通常不需要调用iter()或自己处理迭代器对象.for语句会自动为您执行此操作,创建一个临时的未命名变量,以便在循环期间保存迭代器.另请参见迭代器,序列和生成器.
正如其他人所建议的那样,使用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
Run Code Online (Sandbox Code Playgroud)
结果很奇怪: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'
Run Code Online (Sandbox Code Playgroud)
它现在被认为是一个"真正的"可迭代对象,尽管它在迭代时不能产生任何东西.
那么我误解了什么或者文档不正确吗?
我认为这里的混淆点是,尽管实现__getitem__ 确实允许您迭代对象,但它不是Iterable.
该抽象基类允许虚拟的子类,其中实现了指定的方法(在的情况下,类的形式Iterable,只__iter__),被认为是由isinstance和issubclass是的ABC的子类,即使他们没有明确地从他们继承。但是,它不检查方法实现是否实际有效,只检查它是否提供。
有关更多信息,请参阅PEP-3119,其中介绍了 ABC。
using
isinstance(e, collections.Iterable)是检查对象是否可迭代的最 Pythonic 的方法
我不同意; 我会使用duck-typing并尝试迭代对象。如果对象不可迭代,TypeError则将引发,如果您想处理不可迭代的输入,您可以在函数中捕获它,或者如果不是,则允许渗透到调用者。这完全绕过了对象决定实现迭代的方式,而只是找出它是否在最合适的时间执行。
再补充一点,我认为你引用的文档有点误导。引用iterdocs,这可能会澄清这一点:
object必须是一个支持迭代协议(
__iter__()方法)的集合对象,或者它必须支持序列协议(__getitem__()从 开始的整数参数的方法0)。
这清楚地表明,虽然这两种协议都使对象可迭代,但只有一个是实际的“迭代协议”,而isinstance(thing, Iterable)测试对象正是这个。因此,我们可以得出结论,在最一般的情况下,检查“可以迭代的事物”的一种方法是:
isinstance(thing, (Iterable, Sequence))
Run Code Online (Sandbox Code Playgroud)
尽管这还需要你来实现__len__沿__getitem__到“虚拟子类” Sequence。
| 归档时间: |
|
| 查看次数: |
19506 次 |
| 最近记录: |