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

hasattr(myObj, '__iter__')

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


当前回答

我一直不明白为什么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 3.5开始,你可以使用标准库中的typing模块来做类型相关的事情:

from typing import Iterable

...

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

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

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

在我的代码中,我用来检查非可迭代对象:

hasattr (myobject, __trunc__’)

这非常快,也可以用来检查可迭代对象(使用not)。

我不是100%确定这个解决方案是否适用于所有对象,也许其他可以提供一些更多的背景。__trunc__方法与数值类型相关(所有可以舍入为整数的对象都需要它)。但是我没有发现任何包含__trunc__和__iter__或__getitem__的对象。

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

Hasattr(obj, '__contains__')

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

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

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

注:

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

在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基础结构非常有用。