In Python, how do I determine if an object is iterable?

前端 未结 21 2247
太阳男子
太阳男子 2020-11-22 00:35

Is there a method like isiterable? The only solution I have found so far is to call

hasattr(myObj, \'__iter__\')

But I am not

21条回答
  •  無奈伤痛
    2020-11-22 01:01

    I've been studying this problem quite a bit lately. Based on that my conclusion is that nowadays this is the best approach:

    from collections.abc import Iterable   # drop `.abc` with Python 2.7 or lower
    
    def iterable(obj):
        return isinstance(obj, Iterable)
    

    The above has been recommended already earlier, but the general consensus has been that using iter() would be better:

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

    We've used iter() in our code as well for this purpose, but I've lately started to get more and more annoyed by objects which only have __getitem__ being considered iterable. There are valid reasons to have __getitem__ in a non-iterable object and with them the above code doesn't work well. As a real life example we can use Faker. The above code reports it being iterable but actually trying to iterate it causes an AttributeError (tested with Faker 4.0.2):

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

    If we'd use insinstance(), we wouldn't accidentally consider Faker instances (or any other objects having only __getitem__) to be iterable:

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

    Earlier answers commented that using iter() is safer as the old way to implement iteration in Python was based on __getitem__ and the isinstance() approach wouldn't detect that. This may have been true with old Python versions, but based on my pretty exhaustive testing isinstance() works great nowadays. The only case where isinstance() didn't work but iter() did was with UserDict when using Python 2. If that's relevant, it's possible to use isinstance(item, (Iterable, UserDict)) to get that covered.

提交回复
热议问题