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

hasattr(myObj, '__iter__')

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


当前回答

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

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])来检查它是否为可迭代对象,可能会导致对大型可迭代对象的显著性能损失(特别是如果你只需要可迭代对象的前几个元素,例如),应该避免

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类型的字符串说yes。

Python 3中的bytes对象是可迭代的True == is_iterable(b"string") == is_iterable("string".encode('utf-8')) Python 2和3中的bytearray对象是可迭代的True == is_iterable(bytearray(b"abc"))

O.P. hasattr(x, '__iter__')方法将对Python 3中的字符串说“是”,而在Python 2中对字符串说“否”(无论“或b”或u”)。感谢@LuisMasuelli注意到它也会让你在一个bug __iter__。

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

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

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

考虑到Python的duck类型,最简单的方法是捕捉错误(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())

注:

如果异常类型相同,则区分对象是否不可迭代或已经实现了有bug的__iter__是无关紧要的:无论如何,您将无法迭代对象。 我想我理解你的担忧:如果我也可以依赖鸭类型来引发AttributeError,如果__call__没有为我的对象定义,那么callable如何作为检查存在,但这不是可迭代检查的情况? 我不知道答案,但你可以实现我(和其他用户)给出的函数,或者只是在你的代码中捕获异常(你在那部分的实现将像我写的函数一样——只要确保你将迭代器的创建与其余代码隔离开来,这样你就可以捕获异常并将其与另一个TypeError区分开来。