有没有像isiterable这样的方法?到目前为止,我找到的唯一解决办法就是打电话

hasattr(myObj, '__iter__')

但我不确定这是否万无一失。


当前回答

我在这里找到了一个很好的解决方案:

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

其他回答

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

Hasattr(obj, '__contains__')

它主要检查对象是否实现了in操作符。

优点(其他解决方案都不具备这三个优点):

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

注:

Python的“请求原谅,而不是允许”的哲学在例如,在一个列表中,你有可迭代对象和不可迭代对象,你需要根据它的类型区别对待每个元素(在try上处理可迭代对象,在except上处理不可迭代对象可以工作,但它看起来很丑,会误导人)时,就不会很好地工作了。 对于这个问题的解决方案,试图实际遍历对象(例如[x for x in obj])来检查它是否为可迭代对象,可能会导致对大型可迭代对象的显著性能损失(特别是如果你只需要可迭代对象的前几个元素,例如),应该避免

如果object是可迭代的,下面代码中的isiterable函数将返回True。如果不是iterable则返回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

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

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()起作用的唯一情况是在使用Python 2时使用UserDict。如果这是相关的,可以使用isinstance(item, (Iterable, UserDict))来覆盖。

在我的脚本中,我经常发现定义一个可迭代函数很方便。 (现在合并了Alfe建议的简化):

import collections

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

因此,您可以测试任何对象是否具有非常可读的可迭代形式

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

就像你对可调用函数所做的那样

编辑:如果你安装了numpy,你可以简单地做: 简单地说是什么

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

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

在Python <= 2.5中,你不能也不应该——iterable是一个“非正式的”接口。

但是从Python 2.6和3.0开始,你可以利用新的ABC(抽象基类)基础设施以及一些内置的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将自动识别该类的object为Iterable。

如果你只是期望一个可迭代对象,遍历它,然后忘记它。另一方面,如果您需要根据输入类型执行不同的操作,那么您可能会发现ABC基础结构非常有用。