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

前端 未结 21 2188
太阳男子
太阳男子 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 00:48

    I often find convenient, inside my scripts, to define an iterable function. (Now incorporates Alfe's suggested simplification):

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

    so you can test if any object is iterable in the very readable form

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

    as you would do with thecallable function

    EDIT: if you have numpy installed, you can simply do: from numpy import iterable, which is simply something like

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

    If you do not have numpy, you can simply implement this code, or the one above.

    0 讨论(0)
  • 2020-11-22 00:49

    Duck typing

    try:
        iterator = iter(theElement)
    except TypeError:
        # not iterable
    else:
        # iterable
    
    # for obj in iterator:
    #     pass
    

    Type checking

    Use the Abstract Base Classes. They need at least Python 2.6 and work only for new-style classes.

    from collections.abc import Iterable   # import directly from collections for Python < 3.3
    
    if isinstance(theElement, Iterable):
        # iterable
    else:
        # not iterable
    

    However, iter() is a bit more reliable as described by the documentation:

    Checking isinstance(obj, Iterable) detects classes that are registered as Iterable or that have an __iter__() method, but it does not detect classes that iterate with the __getitem__() method. The only reliable way to determine whether an object is iterable is to call iter(obj).

    0 讨论(0)
  • 2020-11-22 00:50

    This isn't sufficient: the object returned by __iter__ must implement the iteration protocol (i.e. next method). See the relevant section in the documentation.

    In Python, a good practice is to "try and see" instead of "checking".

    0 讨论(0)
  • 2020-11-22 00:50

    Instead of checking for the __iter__ attribute, you could check for the __len__ attribute, which is implemented by every python builtin iterable, including strings.

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

    None-iterable objects would not implement this for obvious reasons. However, it does not catch user-defined iterables that do not implement it, nor do generator expressions, which iter can deal with. However, this can be done in a line, and adding a simple or expression checking for generators would fix this problem. (Note that writing type(my_generator_expression) == generator would throw a NameError. Refer to this answer instead.)

    You can use GeneratorType from types:

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

    --- accepted answer by utdemir

    (This makes it useful for checking if you can call len on the object though.)

    0 讨论(0)
  • 2020-11-22 00:53

    The easiest way, respecting the Python's duck typing, is to catch the error (Python knows perfectly what does it expect from an object to become an iterator):

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

    Notes:

    1. It is irrelevant the distinction whether the object is not iterable, or a buggy __iter__ has been implemented, if the exception type is the same: anyway you will not be able to iterate the object.
    2. I think I understand your concern: How does callable exists as a check if I could also rely on duck typing to raise an AttributeError if __call__ is not defined for my object, but that's not the case for iterable checking?

      I don't know the answer, but you can either implement the function I (and other users) gave, or just catch the exception in your code (your implementation in that part will be like the function I wrote - just ensure you isolate the iterator creation from the rest of the code so you can capture the exception and distinguish it from another TypeError.

    0 讨论(0)
  • 2020-11-22 00:57

    Not really "correct" but can serve as quick check of most common types like strings, tuples, floats, etc...

    >>> '__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 讨论(0)
提交回复
热议问题