有没有像isiterable这样的方法?到目前为止,我找到的唯一解决办法就是打电话
hasattr(myObj, '__iter__')
但我不确定这是否万无一失。
有没有像isiterable这样的方法?到目前为止,我找到的唯一解决办法就是打电话
hasattr(myObj, '__iter__')
但我不确定这是否万无一失。
当前回答
检查__iter__适用于序列类型,但在Python 2中检查字符串会失败。我也想知道正确的答案,在那之前,这里有一种可能性(这也适用于字符串): 试一试: Some_object_iterator = iter(some_object) except TypeError as te: 打印(some_object, 'is not iterable')
内置iter检查__iter__方法,如果是字符串,则检查__getitem__方法。
另一种通用的python方法是假设一个可迭代对象,如果它在给定对象上不起作用,则会优雅地失败。Python术语表:
python编程风格,通过检查对象的方法或属性签名来确定对象的类型,而不是通过与某些类型对象的显式关系(“如果它看起来像鸭子,并且嘎嘎叫得像鸭子,那么它一定是鸭子。”)通过强调接口而不是特定的类型,设计良好的代码通过允许多态替换来提高其灵活性。duck类型避免使用type()或isinstance()进行测试。相反,它通常采用EAFP(请求原谅比请求许可更容易)风格的编程。
...
试一试: _ = (e代表my_object中的e) 除了TypeError: 打印my_object, 'is not iterable'
collections模块提供了一些抽象基类,允许询问类或实例是否提供特定的功能,例如: 从集合。abc import Iterable if isinstance(e, Iterable): # e是可迭代的
但是,这不会检查通过__getitem__可迭代的类。
其他回答
我在这里找到了一个很好的解决方案:
isiterable = lambda obj: isinstance(obj, basestring) \
or getattr(obj, '__iter__', False)
try:
#treat object as iterable
except TypeError, e:
#object is not actually iterable
不要检查你的鸭子是否真的是一只鸭子,看看它是否可迭代,就像它是可迭代的一样对待它,如果不是就抱怨。
在我的代码中,我用来检查非可迭代对象:
hasattr (myobject, __trunc__’)
这非常快,也可以用来检查可迭代对象(使用not)。
我不是100%确定这个解决方案是否适用于所有对象,也许其他可以提供一些更多的背景。__trunc__方法与数值类型相关(所有可以舍入为整数的对象都需要它)。但是我没有发现任何包含__trunc__和__iter__或__getitem__的对象。
这是不够的:__iter__返回的对象必须实现迭代协议(即next方法)。请参阅文档中的相关部分。
在Python中,一个好的实践是“尝试并查看”而不是“检查”。
我想多讲一点iter, __iter__和__getitem__的相互作用,以及幕后发生的事情。有了这些知识,你就能明白为什么你能做到最好
try:
iter(maybe_iterable)
print('iteration will probably work')
except TypeError:
print('not iterable')
我将首先列出事实,然后快速提醒您在python中使用for循环时会发生什么,然后进行讨论以说明事实。
事实
通过调用iter(o)可以从任何对象o中获得迭代器,前提是至少满足以下条件之一:a) o具有__iter__方法,该方法返回一个迭代器对象。迭代器是任何具有__iter__和__next__ (Python 2: next)方法的对象。B) o有__getitem__方法。 对象的实例,或者对象的实例 属性__iter__是不够的。 如果对象o只实现__getitem__,而不实现__iter__,则会构造iter(o) 一个迭代器,试图通过整数索引从o中获取项目,从索引0开始。迭代器将捕获所引发的任何IndexError(但没有其他错误),然后引发StopIteration本身。 在最一般的意义上,没有办法检查iter返回的迭代器是否正常,只能尝试它。 如果对象o实现了__iter__,则iter函数将确保 __iter__返回的对象是一个迭代器。没有健康检查 如果一个对象只实现__getitem__。 __iter__获胜。如果对象o同时实现了__iter__和__getitem__,则iter(o)将调用__iter__。 如果你想让你自己的对象可迭代,总是实现__iter__方法。
for循环
为了继续学习,您需要了解在Python中使用for循环时会发生什么。如果你已经知道了,可以直接跳到下一节。
当你将for item in o用于某个可迭代对象o时,Python调用iter(o)并期望将一个迭代器对象作为返回值。迭代器是任何实现__next__(或Python 2中的next)方法和__iter__方法的对象。
按照惯例,迭代器的__iter__方法应该返回对象本身(即返回self)。然后Python在迭代器上调用next,直到引发StopIteration。所有这些都是隐式发生的,但下面的演示使其可见:
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
使用BasicIterable的实例调用iter将返回一个迭代器,没有任何问题,因为BasicIterable实现了__getitem__。
>>> b = BasicIterable()
>>> iter(b)
<iterator object at 0x7f1ab216e320>
然而,重要的是要注意,b没有__iter__属性,并且不被认为是Iterable或Sequence的实例:
>>> from collections import Iterable, Sequence
>>> hasattr(b, '__iter__')
False
>>> isinstance(b, Iterable)
False
>>> isinstance(b, Sequence)
False
这就是为什么Luciano Ramalho推荐调用iter并处理潜在的TypeError作为检查对象是否可迭代的最准确方法。直接从书中引用:
从Python 3.4开始,检查对象x是否可迭代的最准确方法是调用iter(x),如果不是则处理TypeError异常。这比使用isinstance(x, ABC .Iterable)更准确,因为iter(x)也会考虑遗留的__getitem__方法,而Iterable ABC则不会。
关于第3点:迭代只提供__getitem__而不提供__iter__的对象
在BasicIterable实例上迭代工作如预期:Python 构造一个迭代器,该迭代器尝试按索引获取项目,从0开始,直到引发IndexError。演示对象的__getitem__方法只是返回由iter返回的迭代器作为参数提供给__getitem__(self, item)的项。
>>> b = BasicIterable()
>>> it = iter(b)
>>> next(it)
0
>>> next(it)
1
>>> next(it)
2
>>> next(it)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
请注意,迭代器在无法返回下一项时引发StopIteration,而为item == 3引发的IndexError则在内部处理。这就是为什么使用for循环遍历BasicIterable可以正常工作的原因:
>>> for x in b:
... print(x)
...
0
1
2
下面是另一个例子,目的是让大家了解iter返回的迭代器是如何通过索引访问项目的。WrappedDict不继承dict,这意味着实例不会有__iter__方法。
class WrappedDict(object): # note: no inheritance from dict!
def __init__(self, dic):
self._dict = dic
def __getitem__(self, item):
try:
return self._dict[item] # delegate to dict.__getitem__
except KeyError:
raise IndexError
注意,对__getitem__的调用被委托给dict。__getitem__的方括号符号只是一个简写。
>>> w = WrappedDict({-1: 'not printed',
... 0: 'hi', 1: 'StackOverflow', 2: '!',
... 4: 'not printed',
... 'x': 'not printed'})
>>> for x in w:
... print(x)
...
hi
StackOverflow
!
第4点和第5点:iter在调用__iter__时检查迭代器:
当对对象o调用iter(o)时,iter将确保__iter__的返回值(如果存在该方法)是一个迭代器。这意味着返回的对象 必须实现__next__(或Python 2中的next)和__iter__。Iter不能对只有 提供__getitem__,因为它无法检查对象的项是否可以通过整数索引访问。
class FailIterIterable(object):
def __iter__(self):
return object() # not an iterator
class FailGetitemIterable(object):
def __getitem__(self, item):
raise Exception
注意,从FailIterIterable实例构造迭代器会立即失败,而从FailGetItemIterable实例构造迭代器会成功,但会在第一次调用__next__时抛出异常。
>>> fii = FailIterIterable()
>>> iter(fii)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: iter() returned non-iterator of type 'object'
>>>
>>> fgi = FailGetitemIterable()
>>> it = iter(fgi)
>>> next(it)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/path/iterdemo.py", line 42, in __getitem__
raise Exception
Exception
第6点:__iter__获胜
这一点很简单。如果一个对象实现了__iter__和__getitem__, iter将调用__iter__。考虑下面的类
class IterWinsDemo(object):
def __iter__(self):
return iter(['__iter__', 'wins'])
def __getitem__(self, item):
return ['__getitem__', 'wins'][item]
和循环遍历实例时的输出:
>>> iwd = IterWinsDemo()
>>> for x in iwd:
... print(x)
...
__iter__
wins
第7点:你的可迭代类应该实现__iter__
你可能会问自己,为什么大多数内置序列(如list)都实现__iter__方法,而__getitem__方法就足够了。
class WrappedList(object): # note: no inheritance from list!
def __init__(self, lst):
self._list = lst
def __getitem__(self, item):
return self._list[item]
毕竟,迭代上述类的实例,它委托调用__getitem__列表。__getitem__(使用方括号表示),将正常工作:
>>> wl = WrappedList(['A', 'B', 'C'])
>>> for x in wl:
... print(x)
...
A
B
C
自定义迭代对象应该实现__iter__的原因如下:
如果你实现__iter__,实例将被视为可迭代对象,isinstance(o, collections.abc.Iterable)将返回True。 如果__iter__返回的对象不是迭代器,iter将立即失败并引发TypeError。 __getitem__的特殊处理是出于向后兼容的原因。再次引用Fluent Python:
这就是为什么任何Python序列都是可迭代的:它们都实现了__getitem__。事实上, 标准序列也实现了__iter__,你的也应该实现,因为 由于向后兼容的原因,__getitem__存在特殊的处理 将来会消失(尽管在我写这篇文章时它并没有被弃用)。