from collections.abc import Iterable # import directly from collections for Python < 3.3
if isinstance(theElement, Iterable):
# iterable
else:
# not iterable
Checking isinstance(obj, Iterable) detects classes that are
registered as Iterable or that have an __iter__() method, but
it does not detect classes that iterate with the __getitem__()
method. The only reliable way to determine whether an object
is iterable is to call iter(obj).
import random
class DemoIterable(object):
def __iter__(self):
print('__iter__ called')
return DemoIterator()
class DemoIterator(object):
def __iter__(self):
return self
def __next__(self):
print('__next__ called')
r = random.randint(1, 10)
if r == 5:
print('raising StopIteration')
raise StopIteration
return r
在DemoIterable上迭代:
>>> di = DemoIterable()
>>> for x in di:
... print(x)
...
__iter__ called
__next__ called
9
__next__ called
8
__next__ called
10
__next__ called
3
__next__ called
10
__next__ called
raising StopIteration
讨论和说明
关于第1点和第2点:获取迭代器和不可靠的检查
请考虑以下类别:
class BasicIterable(object):
def __getitem__(self, item):
if item == 3:
raise IndexError
return item
As of Python 3.4, the most accurate way to check whether an object x is iterable is to call iter(x) and handle a TypeError exception if it isn’t. This is more accurate than using isinstance(x, abc.Iterable) , because iter(x) also considers the legacy __getitem__ method, while the Iterable ABC does not.
class FailIterIterable(object):
def __iter__(self):
return object() # not an iterator
class FailGetitemIterable(object):
def __getitem__(self, item):
raise Exception
That is why any Python sequence is iterable: they all implement __getitem__ . In fact,
the standard sequences also implement __iter__, and yours should too, because the
special handling of __getitem__ exists for backward compatibility reasons and may be
gone in the future (although it is not deprecated as I write this).
Pythonic programming style that determines an object's type by inspection of its method or attribute signature rather than by explicit relationship to some type object ("If it looks like a duck and quacks like a duck, it must be a duck.") By emphasizing interfaces rather than specific types, well-designed code improves its flexibility by allowing polymorphic substitution. Duck-typing avoids tests using type() or isinstance(). Instead, it typically employs the EAFP (Easier to Ask Forgiveness than Permission) style of programming.
...
try:
_ = (e for e in my_object)
except TypeError:
print my_object, 'is not iterable'
鸭子打字
类型检查
使用Abstract Base Classes。它们至少需要Python2.6,并且只适用于新样式的类。
但是,
iter()
更可靠,如by the documentation所述:我想对
iter
、__iter__
和__getitem__
之间的相互作用,以及窗帘后面发生的事情,多透露一点。有了这些知识,你就能明白为什么你能做的最好我将首先列出事实,然后快速提醒您在python中使用
for
循环时会发生什么,然后进行讨论以说明事实。事实
如果以下条件中至少有一个成立,则可以通过调用
iter(o)
从任何对象o
获取迭代器:o具有返回迭代器对象的
__iter__
方法。迭代器是具有__iter__
和__next__
(Python 2:next
)方法的任何对象。b)
o
有一个__getitem__
方法。检查
Iterable
或Sequence
的实例,或检查 属性__iter__
不够。如果对象
o
只实现__getitem__
,而不实现__iter__
,则iter(o)
将构造 试图通过整数索引从o
获取项的迭代器,从索引0开始。迭代器将捕获任何引发的IndexError
(但没有其他错误),然后引发StopIteration
本身。在最一般的意义上,除了尝试之外,没有办法检查由
iter
返回的迭代器是否正常。如果对象
o
实现__iter__
,则iter
函数将确保__iter__
返回的对象是迭代器。没有健康检查 如果一个对象只实现__getitem__
。__iter__
获胜。如果对象o
同时实现__iter__
和__getitem__
,则iter(o)
将调用__iter__
。如果您想使您自己的对象可iterable,请始终从abstract base class
Iterable
或其子类之一继承。您必须实现__iter__
方法,否则将像Sequence
那样提供它。for
环为了继续,您需要了解在Python中使用
for
循环时会发生什么。如果你已经知道了,可以直接跳到下一节。当对某个iterable对象使用
for item in o
时,Python将调用iter(o)
,并期望返回一个迭代器对象。迭代器是实现Python 2中的__next__
(或next
)方法和__iter__
方法的任何对象。按照惯例,迭代器的
__iter__
方法应该返回对象本身(即return self
)。然后,Python在迭代器上调用next
,直到引发StopIteration
。所有这些都是隐式发生的,但以下演示使其可见:在
DemoIterable
上迭代:讨论和说明
关于第1点和第2点:获取迭代器和不可靠的检查
请考虑以下类别:
使用实例
BasicIterable
调用iter
将返回一个迭代器,不会有任何问题,因为BasicIterable
实现了__getitem__
。但是,需要注意的是,
b
没有__iter__
属性,并且不被视为Iterable
或Sequence
的实例:这就是为什么Luciano Ramalho的Fluent Python建议调用
iter
并处理潜在的TypeError
作为检查对象是否可iterable的最精确方法。直接从书中引用:在第3点:迭代仅提供
__getitem__
,但不提供__iter__
的对象对
BasicIterable
的实例进行迭代可以按预期工作:Python 构造一个迭代器,该迭代器尝试从零开始按索引获取项,直到引发IndexError
。demo对象的__getitem__
方法只返回由iter
返回的迭代器作为__getitem__(self, item)
的参数提供的item
。注意,迭代器在不能返回下一个i时引发循环遍历
StopIteration
以及为item == 3
引发的IndexError
在内部处理。这就是为什么用^{BasicIterable
的原因:这里是另一个例子,目的是让我们了解
iter
返回的迭代器如何通过索引访问项。WrappedDict
不是从dict
继承的,这意味着实例没有__iter__
方法。注意,对
__getitem__
的调用被委托给dict.__getitem__
,而方括号表示法只是一种简写。在第4点和第5点:
iter
当迭代器调用__iter__
时检查迭代器:当为对象
o
调用iter(o)
时,iter
将确保__iter__
的返回值(如果方法存在)是迭代器。这意味着返回的对象 必须实现__next__
(或Python 2中的next
)和__iter__
。iter
无法对仅 提供__getitem__
,因为它无法检查对象的项是否可以通过整数索引访问。请注意,从
FailIterIterable
实例构造迭代器会立即失败,而从FailGetItemIterable
实例构造迭代器会成功,但会在第一次调用__next__
时引发异常。在第6点:
__iter__
获胜这个很简单。如果对象实现
__iter__
和__getitem__
,则iter
将调用__iter__
。考虑下一个类以及在实例上循环时的输出:
关于第7点:iterable类应该实现
__iter__
您可能会问自己,为什么像
list
这样的大多数内置序列在__getitem__
足够时实现__iter__
方法。毕竟,对上述类的实例进行迭代,将调用委托给
__getitem__
到list.__getitem__
(使用方括号表示法),可以很好地工作:自定义iterable应该实现
__iter__
的原因如下:__iter__
,实例将被视为可访问的,并且isinstance(o, collections.abc.Iterable)
将返回True
。__iter__
返回的对象不是迭代器,iter
将立即失败并引发一个TypeError
。__getitem__
的特殊处理。再次引用Fluent Python:检查
__iter__
对序列类型有效,但对Python 2中的strings会失败。我也想知道正确的答案,在那之前,有一种可能性(也适用于字符串):内置的
iter
检查__iter__
方法,如果是字符串,则检查__getitem__
方法。另一种常见的pythonic方法是假设一个iterable,如果它不能在给定的对象上工作,那么它将优雅地失败。Python词汇表:
^{} 模块提供一些抽象基类,这些基类允许询问类或实例是否提供特定功能,例如:
但是,这不会检查可通过
__getitem__
访问的类。相关问题 更多 >
编程相关推荐