python判断一个对象是否可迭代_在Python中,如何确定对象是否是可迭代的?

我想对…的相互作用有一点了解。iter, __iter__和__getitem__窗帘后面发生了什么。有了这些知识,你就能理解为什么你能做的最好的就是try:

iter(maybe_iterable)

print('iteration will probably work')except TypeError:

print('not iterable')

我将首先列出事实,然后快速提醒您使用for循环在python中,然后进行讨论以说明事实。

事实您可以从任何对象获得迭代器。o打电话iter(o)如果至少有下列条件之一有效:

a)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__.

如果希望使自己的对象可迭代,请始终实现__iter__方法。

for环

为了跟进,您需要了解使用for在Python中循环。如果你已经知道了,可以跳到下一节。

当你使用for item in o对于一些可迭代对象o,Python调用iter(o)并期望迭代器对象作为返回值。迭代器是实现__next__(或next方法和__iter__方法。

根据惯例,__iter__迭代器的方法应该返回对象本身(即return self)。Python然后调用next在迭代器上直到StopIteration已经长大了。所有这些都是隐式的,但下面的演示使其可见:import randomclass 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__ called9__next__ called8__next__ called10__next__ called3__next__ called10__next__ called

raising StopIteration

讨论和说明

关于第1点和第2点:获得迭代器和不可靠的检查

考虑以下课程:class BasicIterable(object):

def __getitem__(self, item):

if item == 3:

raise IndexError

return item

呼叫iter的实例BasicIterable将返回迭代器,而不会出现任何问题,因为BasicIterable实施器__getitem__.>>> b = BasicIterable()>>> iter(b)

然而,重要的是要注意b没有__iter__属性,并且不被视为Iterable或Sequence:>>> from collections import Iterable, Sequence>>> hasattr(b, '__iter__')False>>> isinstance(b, Iterable)False>>> isinstance(b, Sequence)False

这就是为什么流利Python卢西亚诺·拉马略建议iter和处理潜在的TypeError作为检查对象是否可迭代的最准确的方法。直接引用这本书:在Python3.4中,检查对象是否是最准确的方法x是可迭代的,是调用iter(x)处理一个TypeError异常。这比使用isinstance(x, abc.Iterable),因为iter(x)也会考虑遗产__getitem__方法,而IterableABC不知道。

关于第3点:迭代仅提供__getitem__,但不是__iter__

的实例进行迭代。BasicIterable按预期工作:Python构造一个迭代器,该迭代器尝试按索引获取项,从零开始,直到IndexError已经长大了。演示对象的__getitem__方法只返回item作为参数提供给__getitem__(self, item)返回的迭代器iter.>>> b = BasicIterable()>>> it = iter(b)>>> next(it)0>>> next(it)1>>> next(it)2>>> next(it)Traceback (most recent call last):

File "", line 1, in StopIteration

注意,迭代器引发StopIteration如果它不能返回下一项,并且IndexError为item == 3内部处理。这就是为什么在BasicIterable带着for循环按预期工作:>>> for x in b:...     print(x)...012

下面是另一个示例,以了解迭代器是如何由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)... hiStackOverflow!

关于第4和第5点:iter当迭代器调用__iter__:

什么时候iter(o)为对象调用o, iter的返回值__iter__,如果存在该方法,则为迭代器。这意味着返回的对象必须实现__next__(或next(在Python 2中)和__iter__. iter无法对仅提供__getitem__,因为它无法检查对象的项是否可以通过整数索引访问。class FailIterIterable(object):

def __iter__(self):

return object() # not an iteratorclass FailGetitemIterable(object):

def __getitem__(self, item):

raise Exception

注意,从FailIterIterable实例的迭代器时,实例会立即失败。FailGetItemIterable成功,但将在第一次调用__next__.>>> fii = FailIterIterable()>>> iter(fii)Traceback (most recent call last):

File "", line 1, in TypeError: iter() returned non-iterator of type 'object'>>>>>> fgi = FailGetitemIterable()

>>> it = iter(fgi)>>> next(it)Traceback (most recent call last):

File "", line 1, in 

File "/path/iterdemo.py", line 42, in __getitem__    raise ExceptionException

关于第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__到list.__getitem__(使用方括号符号),工作正常:>>> wl = WrappedList(['A', 'B', 'C'])>>> for x in wl:...     print(x)... A

B

C

您的自定义迭代应该实现的原因__iter__如下:如果你实现

__iter__,实例将被视为可迭代的,并且

isinstance(o, collections.Iterable)会回来

True.

返回的对象

__iter__不是迭代器,

iter将立即失败并引发

TypeError.

特殊处理

__getitem__由于向后兼容的原因而存在。再次引用流利的Python:这就是为什么任何Python序列都是可迭代的:它们都实现了__getitem__..实际上,标准序列还实现了__iter__,而你的也应该如此,因为对…的特殊处理。__getitem__因为向后兼容性的原因而存在,而且将来可能会消失(尽管在我写这个时,它并不是不推荐的)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值