在Python中,如何确定对象是否可迭代?


1083

有没有类似的方法isiterable?到目前为止,我发现的唯一解决方案是致电

hasattr(myObj, '__iter__')

但是我不确定这有多愚蠢。


18
__getitem__也足以使对象变得可迭代
Kos

4
FWIW:iter(myObj)如果成功,则为isinstance(myObj, dict),因此,如果您正在查看myObj可能是dicts或单个序列的a dict,则在两种情况下都将成功。如果您想知道什么是序列而什么不是序列,那么微妙是很重要的。(在Python 2中)
Ben Mosher 2014年

7
__getitem__如果对象从零索引开始,也足以使它可迭代。
卡洛斯·A·戈麦斯

Answers:


26

我最近一直在研究这个问题。基于此,我的结论是,如今这是最好的方法:

from collections.abc import Iterable   # drop `.abc` with Python 2.7 or lower

def iterable(obj):
    return isinstance(obj, Iterable)

上面已经建议过上述方法,但是普遍的共识是使用iter()会更好:

def iterable(obj):
    try:
        iter(obj)
    except Exception:
        return False
    else:
        return True

我们也iter()为此目的在代码中使用了,但是最近我开始越来越多地被那些只__getitem__被认为是可迭代的对象而烦恼。有__getitem__一个不可迭代的对象有充分的理由,并且上面的代码不能与它们很好地配合。作为真实示例,我们可以使用Faker。上面的代码报告了它是可迭代的,但实际上尝试对其进行迭代会导致AttributeError(用Faker 4.0.2测试):

>>> from faker import Faker
>>> fake = Faker()
>>> iter(fake)    # No exception, must be iterable
<iterator object at 0x7f1c71db58d0>
>>> list(fake)    # Ooops
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/.../site-packages/faker/proxy.py", line 59, in __getitem__
    return self._factory_map[locale.replace('-', '_')]
AttributeError: 'int' object has no attribute 'replace'

如果使用insinstance(),我们不会偶然地认为Faker实例(或其他只有的对象__getitem__)是可迭代的:

>>> from collections.abc import Iterable
>>> from faker import Faker
>>> isinstance(Faker(), Iterable)
False

较早的答案评论说,使用iter()安全性更高,因为在Python中实现迭代的旧方法基于__getitem__isinstance()方法,而该方法无法检测到这一点。在旧的Python版本中可能确实如此,但是根据我相当详尽的测试,isinstance()如今可以很好地工作了。唯一isinstance()不起作用但起作用的情况iter()UserDict使用Python2。如果相关,则可以使用isinstance(item, (Iterable, UserDict))它进行覆盖。


1
typing.Dict被认为是可迭代的,iter(Dict)但会list(Dict)因错误而失败TypeError: Parameters to generic types must be types. Got 0.。如预期isinstance(Dict, Iterable)返回false。
PekkaKlärck

1
我得出了相同的结论,只是出于不同的原因。使用iter导致我们的某些使用“预缓存”的代码不必要地减慢了速度。如果__iter__代码很慢,那么iter只要您只想查看某些事情是否可迭代,就会调用...。
thorwhalen

842
  1. 检查__iter__序列类型是否有效,但是在Python 2中,例如字符串可能会失败。我也想知道正确的答案,在此之前,这是一种可能性(也适用于字符串):

    from __future__ import print_function
    
    try:
        some_object_iterator = iter(some_object)
    except TypeError as te:
        print(some_object, 'is not iterable')

    所述iter内置的检查的__iter__方法或串的情况下的__getitem__方法。

  2. 另一种通用的pythonic方法是假定一个可迭代的对象,如果它不适用于给定的对象,则将优雅地失败。Python词汇表:

    通过检查对象的方法或属性签名而不是通过与某种类型对象的显式关系来确定对象类型的Python编程风格(“如果它看起来像鸭子,并且像鸭子一样嘎嘎叫,那一定是鸭子。”)通过强调接口经过精心设计的代码(而不是特定类型)通过允许多态替换来提高其灵活性。鸭式输入避免使用type()或isinstance()进行测试。取而代之的是,它通常采用EAFP(比授权更容易获得宽恕)风格的编程。

    ...

    try:
       _ = (e for e in my_object)
    except TypeError:
       print my_object, 'is not iterable'
  3. collections模块提供了一些抽象基类,这些基类允许询问类或实例是否提供了特定的功能,例如:

    from collections.abc import Iterable
    
    if isinstance(e, Iterable):
        # e is iterable

    但是,这不会检查可通过迭代的类__getitem__


34
[e for e in my_object]可能由于其他原因引发异常,即my_object未定义或实现中可能存在的错误my_object
Nick Dandoulakis 09年

37
字符串一个序列(isinstance('', Sequence) == True并且任何序列都是可迭代的(isinstance('', Iterable))。虽然hasattr('', '__iter__') == False,这可能会造成混淆。
jfs

82
如果my_object非常大(例如,无限itertools.count()),则列表理解将占用大量时间/内存。最好生成一个生成器,它永远不会尝试构建(可能是无限的)列表。
克里斯·卢茨

14
如果some_object也因其他原因(错误等)引发TypeError怎么办?如何从“不可迭代的TypeError”中分辨出来?
肖恩(Shaung)2011年

54
请注意,在Python 3中:hasattr(u"hello", '__iter__')返回True
Carlos Carlos

572

鸭打字

try:
    iterator = iter(theElement)
except TypeError:
    # not iterable
else:
    # iterable

# for obj in iterator:
#     pass

类型检查

使用抽象基类。他们至少需要Python 2.6,并且仅适用于新型类。

from collections.abc import Iterable   # import directly from collections for Python < 3.3

if isinstance(theElement, Iterable):
    # iterable
else:
    # not iterable

但是,iter()文档所描述那样更可靠:

检查isinstance(obj, Iterable)会检测已注册为Iterable或具有__iter__()方法的类,但不会检测到使用该__getitem__() 方法进行迭代的类。确定对象是否可迭代的唯一可靠方法是调用iter(obj)


18
摘自Luciano Ramalho的“ Fluent Python”:从Python 3.4开始,检查对象x是否可迭代的最准确方法是调用iter(x)并处理一个TypeError异常(如果不是)。这比使用isinstance(x,abc.Iterable)更准确,因为iter(x)还考虑了传统的getitem方法,而Iterable ABC则不考虑。
RdB

如果您在考虑“哦,我会isinstance(x, (collections.Iterable, collections.Sequence))代替iter(x)”,请注意,这仍然不会检测到仅实现__getitem__而不是实现的可迭代对象__len__。使用iter(x)并捕获异常。
戴尔

您的第二个答案不起作用。在PyUNO中,如果我这样做的话iter(slide1),一切都会顺利进行,但是会isinstance(slide1, Iterable)抛出异常TypeError: issubclass() arg 1 must be a class
Hi-Angel

@ Hi-Angel听起来像是一个错误,PyUNO请注意您的错误消息中显示的issubclass()不是isinstance()
格奥尔格·舍利(GeorgSchölly),

2
在一个对象上调用iter()可能是一项昂贵的操作(请参阅Pytorch中的DataLoader,它将在iter()上派生/产生多个进程)。
szali

125

我想摆脱一点点的相互作用越轻iter__iter____getitem__会发生什么窗帘后面。有了这些知识,您将能够理解为什么您能做到最好的是

try:
    iter(maybe_iterable)
    print('iteration will probably work')
except TypeError:
    print('not iterable')

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

事实

  1. 如果至少满足以下条件之一,则可以o通过调用从任何对象获取迭代器iter(o)

    a)o具有__iter__返回迭代器对象的方法。迭代器是任何具有__iter__和方法__next__(Python 2 :)的对象next

    b)o__getitem__方法。

  2. 仅检查Iterable或的实例Sequence,或仅检查属性__iter__是不够的。

  3. 如果一个对象o仅实现__getitem__,而不是实现__iter__iter(o)则将构造一个迭代器,该迭代器尝试从o整数索引(从索引0开始)获取项目。迭代器将捕获IndexError所引发的任何(但无其他错误),然后引发StopIteration自身。

  4. 从最一般的意义上讲,iter除了尝试一下之外,没有其他方法可以检查返回的迭代器是否正常。

  5. 如果o实现了对象__iter__,则该iter函数将确保返回的对象__iter__是迭代器。如果对象仅实现,则没有健全性检查__getitem__

  6. __iter__胜。如果一个对象同时o实现__iter__and __getitem__iter(o)将调用__iter__

  7. 如果要使自己的对象可迭代,请始终实现该__iter__方法。

for 循环

为了继续学习,您需要了解for在Python中使用循环时会发生什么。如果您已经知道,请随时跳到下一部分。

for item in o用于某些可迭代对象时o,Python调用iter(o)并期望将迭代器对象作为返回值。迭代器是实现__next__(或next在Python 2中)方法和__iter__方法的任何对象。

按照约定,__iter__迭代器的方法应返回对象本身(即return self)。然后next,Python调用迭代器,直到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

调用iter的实例BasicIterable将返回迭代器,而不会出现任何问题,因为BasicIterableImplements __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的Fluent Python建议调用iter和处理潜能TypeError作为检查对象是否可迭代的最准确方法。直接从书中引用:

从Python 3.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 "<stdin>", line 1, in <module>
StopIteration

请注意,当迭代器StopIteration无法返回下一项时,它将引发该迭代器IndexError,并且为其item == 3内部处理。这就是为什么按预期BasicIterable进行for循环的原因:

>>> 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

注意,将to __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__

iter(o)为对象调用时oiter将确保方法的返回值(__iter__如果存在)是迭代器。这意味着返回的对象必须实现__next__(或next在Python 2中)和__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,但会在第一次调用时引发Exception __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__and __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__如下:

  1. 如果实现__iter__,则实例将被视为可迭代的,isinstance(o, collections.abc.Iterable)并将返回True
  2. 如果返回的对象__iter__不是迭代器,iter则将立即失败并引发TypeError
  3. 由于__getitem__向后兼容的原因,存在的特殊处理。再次引用Fluent Python:

这就是任何Python序列都是可迭代的原因:它们都实现了__getitem__。实际上,标准序列也可以实现__iter__,您也应该实现,因为__getitem__出于向后兼容的原因而存在对的特殊处理,并且可能在将来消失(尽管在我撰写本文时不推荐使用)。


所以is_iterable通过Truetry块和块中返回来定义谓词是安全Falseexcept TypeError吗?
alancalvitti,

这是一个很好的答案。我认为它突出了getitem协议的不直观和不幸的性质。永远不应该添加它。
Neil G

31

这还不够:返回的对象__iter__必须实现迭代协议(即next方法)。请参阅文档中的相关部分。

在Python中,一个好的做法是“尝试一下”而不是“检查”。


9
我相信“鸭子打字”吗?:)
willem

9
@willem:或“不求许可,但
求饶

14
@willem“权限”和“宽恕”两种样式都可以视为鸭子打字。如果你问一个对象可以,而不是它什么,这是鸭打字。如果您使用自省,那就是“权限”;如果您只是尝试这样做,看看它是否有效,那就是“宽恕”。
马克·里德

22

在Python <= 2.5中,您不能也不应-可迭代是一个“非正式”接口。

但是从Python 2.6和3.0开始,您可以利用新的ABC(抽象基类)基础结构以及一些内置的ABC(可在collections模块中使用):

from collections import Iterable

class MyObject(object):
    pass

mo = MyObject()
print isinstance(mo, Iterable)
Iterable.register(MyObject)
print isinstance(mo, Iterable)

print isinstance("abc", Iterable)

现在,这是合乎需要的还是实际可行的,仅是一个约定问题。如您所见,您可以将一个不可迭代的对象注册为Iterable-它将在运行时引发异常。因此,isinstance获得了“新”的含义-它只是检查“声明的”类型兼容性,这是在Python中使用的好方法。

另一方面,如果您的对象不满足您所需的接口,您将要做什么?请看以下示例:

from collections import Iterable
from traceback import print_exc

def check_and_raise(x):
    if not isinstance(x, Iterable):
        raise TypeError, "%s is not iterable" % x
    else:
        for i in x:
            print i

def just_iter(x):
    for i in x:
        print i


class NotIterable(object):
    pass

if __name__ == "__main__":
    try:
        check_and_raise(5)
    except:
        print_exc()
        print

    try:
        just_iter(5)
    except:
        print_exc()
        print

    try:
        Iterable.register(NotIterable)
        ni = NotIterable()
        check_and_raise(ni)
    except:
        print_exc()
        print

如果对象不满足您的期望,则只引发TypeError,但是如果已注册了正确的ABC,则您的检查无用。相反,如果该__iter__方法可用,Python会自动将该类的对象识别为Iterable。

因此,如果您只是期望一个可迭代的对象,请对其进行迭代并忘记它。另一方面,如果您需要根据输入类型执行不同的操作,则可能会发现ABC基础结构非常有用。


13
except:对于初学者,请勿在示例代码中使用裸露。它会提倡不良做法。
jfs

JFS:我不会,但是我需要遍历多个引发异常的代码,并且我不想捕获特定的异常……我认为这段代码的目的很明确。
艾伦·弗兰佐尼

21
try:
  #treat object as iterable
except TypeError, e:
  #object is not actually iterable

不要运行检查以查看您的鸭子是否真的是鸭子,以查看它是否可迭代,请像对待鸭子一样对待它,否则请抱怨。


3
从技术上讲,在迭代过程中,您的计算可能会抛出a TypeError并导致您离开这里,但基本上是这样。
克里斯·卢茨

6
@willem:请使用timeit进行基准测试。Python异常通常比if语句更快。他们可以通过翻译者走的路更短。
S.Lott

2
@willem:IronPython具有慢速(与CPython相比)的异常。
jfs

2
一个可行的try:语句确实非常快。因此,如果您有少数例外,那么try-except很快。如果您预计会有许多例外情况,则“如果”会更快。
Arne Babenhauserheide 2012年

2
不应该通过as e在后面添加“ ” TypeError而不是添加“ ” 来捕获异常对象, e吗?
HelloGoodbye

21

Python 3.5开始,您可以使用标准库中的类型输入模块来处理与类型相关的事情:

from typing import Iterable

...

if isinstance(my_item, Iterable):
    print(True)

18

到目前为止,我发现的最佳解决方案是:

hasattr(obj, '__contains__')

基本上检查对象是否实现了in运算符。

优点(其他解决方案都没有这三个优点):

  • 它是一个表达式(作为lambda,而不是try ... except变体)
  • (应该)由所有可迭代对象(包括字符串)实现(而不是__iter__
  • 适用于任何Python> = 2.5

笔记:

  • 例如,在列表中同时具有可迭代和不可迭代,并且您需要根据其类型对每个元素进行不同处理(在try和non-上处理可迭代)时,Python的“请求宽恕,而不是允许”的哲学无法很好地工作。上的iterables 起作用,但是看起来很难看并且具有误导性)
  • 尝试实际迭代对象(例如[x for obj中的x的x])以检查其是否可迭代的问题的解决方案,可能会导致大型可迭代对象的性能下降(尤其是如果您只需要可迭代对象的前几个元素,则为示例),应避免

3
很好,但是为什么不使用stackoverflow.com/questions/1952464/…中提出的collections模块呢?对我来说似乎更具表现力。
戴夫·亚伯拉罕斯

1
它更短(并且不需要额外的导入)而又不会失去任何清晰度:拥有“包含”方法似乎是一种检查某物是否为对象集合的自然方法。
弗拉德

46
仅仅因为某物可以包含某物并不一定意味着它是可迭代的。例如,用户可以检查点是否在3D立方体中,但是如何遍历此对象?
Casey Kuball 2012年

13
这是不正确的。迭代器本身不支持contains,至少在Python 3.4中是这样。
彼得·辛纳斯

15

您可以尝试以下方法:

def iterable(a):
    try:
        (x for x in a)
        return True
    except TypeError:
        return False

如果我们可以使生成器在其上进行迭代(但不要使用生成器,这样就不会占用空间),那么它是可迭代的。好像是“ duh”之类的东西。为什么首先需要确定变量是否可迭代?


iterable(itertools.repeat(0))呢 :)
badp

5
@badp,(x for x in a)仅创建一个生成器,它不对a进行任何迭代。
catchmeifyoutry

5
尝试(x for x in a)等同于尝试iterator = iter(a)吗?还是在某些情况下两者不同?
最大

是不是for _ in a: break更简单?慢一点吗?
Mr_and_Mrs_D 2015年

2
@Mr_and_Mrs_D不好,如果被测试的对象是随后进行迭代的迭代器(由于无法重置其位置,它将缩短1个项目),创建垃圾生成器不会在对象上进行迭代,因为它们不会被迭代,尽管我不确定如果不能迭代,它将100%引发TypeError。
Tcll

13

我在这里找到了一个不错的解决方案:

isiterable = lambda obj: isinstance(obj, basestring) \
    or getattr(obj, '__iter__', False)

10

根据Python 2词汇表,可迭代项是

所有序列类型(如liststr,和tuple)和一些非序列类型,如dictfile以及你与定义任何类的对象__iter__()__getitem__()方法。Iterables可用于for循环以及需要序列的许多其他地方(zip(),map()等)。将可迭代对象作为参数传递给内置函数iter()时,它将返回该对象的迭代器。

当然,考虑到Python的通用编码风格是基于“请求宽容比允许容易”这一事实,因此,人们普遍期望使用

try:
    for i in object_in_question:
        do_something
except TypeError:
    do_something_for_non_iterable

但是,如果您需要显式检查它,可以通过测试一个可迭代对象hasattr(object_in_question, "__iter__") or hasattr(object_in_question, "__getitem__")。您需要检查两者,因为strs没有__iter__方法(至少在Python 2中没有),并且generator对象没有__getitem__方法。


8

我经常在脚本内找到定义iterable函数的方便方法。(现在结合了Alfe建议的简化):

import collections

def iterable(obj):
    return isinstance(obj, collections.Iterable):

因此,您可以以可读性强的形式测试任何对象是否可迭代

if iterable(obj):
    # act on iterable
else:
    # not iterable

就像您使用该callable功能一样

编辑:如果您已安装numpy,则可以执行以下操作:from numpy import iterable,这就像

def iterable(obj):
    try: iter(obj)
    except: return False
    return True

如果您没有numpy,则可以简单地实现此代码或上面的代码。


3
每当您做某事if x: return True else: return False(使用x布尔值)时,都可以将其写为return x。你的情况return isinstance(…)没有任何if
阿尔夫,

既然您承认Alfe的解决方案更好,那么为什么不编辑答案以简单地说出来呢?相反,您现在在答案中有两个版本。不必要的冗长。提交修改以解决此问题。
ToolmakerSteve

2
您应该在`例外中捕获“ TypeError”:return False`行。赶上一切都是一种坏模式。
Mariusz Jamro 2014年

我知道。我从NumPy库中翻译了这段代码,该库使用了通用异常。
fmonegaglia 2014年

仅仅是因为代码是从NumPy中获取的,并不意味着它是好的……模式,捕获所有事情的唯一时间就是您在程序内部明确地进行了错误处理。
Tcll

5

具有这样的内置功能:

from pandas.util.testing import isiterable

然而,这只是看是否存在,__iter__而不是真正在乎序列和类似物。
EAD

4

它总是躲避我,为什么Python有callable(obj) -> bool,但不是iterable(obj) -> bool......
当然这是容易做到hasattr(obj,'__call__'),即使是速度较慢。

由于几乎所有其他答案都建议使用try/ except TypeError,因此在任何语言中通常都将异常测试视为不良实践,因此,以下是iterable(obj) -> bool我越来越喜欢并经常使用的实现:

为了python 2的缘故,我将只使用lambda来提高性能……
(在python 3中,定义函数的功能def与无关紧要lambda

iterable = lambda obj: hasattr(obj,'__iter__') or hasattr(obj,'__getitem__')

请注意,此功能对于的对象执行得更快,__iter__因为它不会测试__getitem__

大多数可迭代对象都应依赖于__iter__特殊情况对象回退到的位置__getitem__,尽管要使对象可迭代则需要使用任一个。
(由于这是标准的,因此也会影响C对象)


他没有提供有效的代码,更不用说python的性能了……尽管这个答案只是为了方便起见,就像我在这里看到的很多次一样。
Tcll

3
def is_iterable(x):
    try:
        0 in x
    except TypeError:
        return False
    else:
        return True

这将对所有可迭代对象的方式都说“是”,但是对Python 2中的字符串说“否”。(例如,当递归函数可以使用字符串或字符串容器时,这就是我想要的。在这种情况下,请求宽恕可能会导致混淆代码,最好先请求权限。)

import numpy

class Yes:
    def __iter__(self):
        yield 1;
        yield 2;
        yield 3;

class No:
    pass

class Nope:
    def __iter__(self):
        return 'nonsense'

assert is_iterable(Yes())
assert is_iterable(range(3))
assert is_iterable((1,2,3))   # tuple
assert is_iterable([1,2,3])   # list
assert is_iterable({1,2,3})   # set
assert is_iterable({1:'one', 2:'two', 3:'three'})   # dictionary
assert is_iterable(numpy.array([1,2,3]))
assert is_iterable(bytearray("not really a string", 'utf-8'))

assert not is_iterable(No())
assert not is_iterable(Nope())
assert not is_iterable("string")
assert not is_iterable(42)
assert not is_iterable(True)
assert not is_iterable(None)

这里的许多其他策略都会对字符串说“是”。如果您要使用它们,请使用它们。

import collections
import numpy

assert isinstance("string", collections.Iterable)
assert isinstance("string", collections.Sequence)
assert numpy.iterable("string")
assert iter("string")
assert hasattr("string", '__getitem__')

注意:is_iterable()将对类型为bytes和的字符串说是bytearray

  • bytesPython 3中的对象是可迭代的。Python2 True == is_iterable(b"string") == is_iterable("string".encode('utf-8'))中没有此类。
  • bytearray Python 2和3中的对象是可迭代的 True == is_iterable(bytearray(b"abc"))

该任择议定书hasattr(x, '__iter__')的做法将是说在Python 3,没有在Python 2串(也罢''b''u'')。感谢@LuisMasuelli注意到它也会让您失望__iter__


2

尊重Python的鸭子类型,最简单的方法是捕获错误(Python完全知道从对象变成迭代器的期望):

class A(object):
    def __getitem__(self, item):
        return something

class B(object):
    def __iter__(self):
        # Return a compliant iterator. Just an example
        return iter([])

class C(object):
    def __iter__(self):
        # Return crap
        return 1

class D(object): pass

def iterable(obj):
    try:
        iter(obj)
        return True
    except:
        return False

assert iterable(A())
assert iterable(B())
assert iterable(C())
assert not iterable(D())

注意事项

  1. __iter__如果异常类型相同,则对象是不可迭代的还是已实现越野车的区分是无关紧要的:无论如何,您将无法迭代该对象。
  2. 我想我理解您的担心:callable如果我还可以依赖于鸭子类型来引发未为我的对象定义的AttributeErrorif 的检查,那么它如何存在__call__,但可迭代检查不是这种情况?

    我不知道答案,但是您可以实现我(和其他用户)提供的功能,也可以仅捕获代码中的异常(您在该部分中的实现将类似于我编写的功能-只要确保隔离从代码的其余部分创建迭代器,这样您可以捕获异常并将其与另一个区别TypeError


1

如果对象是可迭代的isiterable,则以下代码中的func返回True。如果不是迭代返回False

def isiterable(object_):
    return hasattr(type(object_), "__iter__")

fruits = ("apple", "banana", "peach")
isiterable(fruits) # returns True

num = 345
isiterable(num) # returns False

isiterable(str) # returns False because str type is type class and it's not iterable.

hello = "hello dude !"
isiterable(hello) # returns True because as you know string objects are iterable

2
上面有这么多详细的答案,但有很多反对意见,您却给出了无法解释的答案...
Meh

请不要发布裸代码。还包括对此的解释。
乔纳森·米

1

除了检查__iter__属性之外,您还可以检查__len__属性,该属性由每个内置迭代的python实现,包括字符串。

>>> hasattr(1, "__len__")
False
>>> hasattr(1.3, "__len__")
False
>>> hasattr("a", "__len__")
True
>>> hasattr([1,2,3], "__len__")
True
>>> hasattr({1,2}, "__len__")
True
>>> hasattr({"a":1}, "__len__")
True
>>> hasattr(("a", 1), "__len__")
True

不可迭代的对象出于明显的原因不会实现此目的。但是,它不会捕获没有实现它的用户定义的可迭代对象,也不会捕获iter可以处理的生成器表达式。但是,这可以一行完成,并且or为生成器添加一个简单的表达式检查将解决此问题。(请注意,写作type(my_generator_expression) == generator会引发NameError。请改为参考答案。)

您可以从以下类型使用GeneratorType:

>>> import types
>>> types.GeneratorType
<class 'generator'>
>>> gen = (i for i in range(10))
>>> isinstance(gen, types.GeneratorType)
True

--- utdemir接受的答案

(这对于检查是否可以调用len该对象很有用。)


不幸的是,并非所有可迭代对象都使用__len__...在这种情况下,通常是不正确地使用了两个对象之间的距离计算。在哪里obj.dist()可以轻松替换。
Tcll

是的 大多数用户定义的可迭代程序实现iter和getitem,但不实现len。但是,内置类型可以,并且如果要检查是否可以调用len函数,则检查len更为安全。但是你是对的。
DarthCadeus

0

并不是真正的“正确”,但可以用作最常见类型的快速检查,例如字符串,元组,浮点数等。

>>> '__iter__' in dir('sds')
True
>>> '__iter__' in dir(56)
False
>>> '__iter__' in dir([5,6,9,8])
True
>>> '__iter__' in dir({'jh':'ff'})
True
>>> '__iter__' in dir({'jh'})
True
>>> '__iter__' in dir(56.9865)
False

0

Kinda参加聚会很晚,但是我问了自己这个问题,然后看到了这个答案。我不知道是否有人已经发布了这个。但从本质上讲,我注意到所有可迭代类型的字典中都具有__getitem __()。这是您无需尝试即可检查对象是否可迭代的方式。(双关语意)

def is_attr(arg):
    return '__getitem__' in dir(arg)

不幸的是,这是不可靠的。示例
timgeb

1
设置对象是另一个反例。
Raymond Hettinger
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.