1404

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

hasattr(myObj, '__iter__')

But I am not sure how fool-proof this is.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
willem
  • 25,977
  • 22
  • 75
  • 115
  • 25
    `__getitem__` is also sufficient to make an object iterable – Kos Jul 02 '12 at 14:58
  • 4
    FWIW: `iter(myObj)` succeeds if `isinstance(myObj, dict)`, so if you're looking at a `myObj` that could be a sequence of `dict`s or a single `dict`, you'll succeed in both cases. A subtlety that is important if you want to know what's a sequence and what isn't. (in Python 2) – Ben Mosher Jul 25 '14 at 15:10
  • 11
    `__getitem__` is also sufficient to make an object iterable ... **if it starts at zero index**. – Carlos A. Gómez Jul 08 '17 at 10:16
  • `__getitem__` is sufficient but not necessary. E.g. a generator object is iterable but it does not have the `__getitem__`. – Max Jul 05 '23 at 23:22

24 Answers24

1026
  1. Checking for __iter__ works on sequence types, but it would fail on e.g. strings in Python 2. I would like to know the right answer too, until then, here is one possibility (which would work on strings, too):

    try:
        some_object_iterator = iter(some_object)
    except TypeError as te:
        print(some_object, 'is not iterable')
    

    The iter built-in checks for the __iter__ method or in the case of strings the __getitem__ method.

  2. Another general pythonic approach is to assume an iterable, then fail gracefully if it does not work on the given object. The Python glossary:

    Pythonic programming style that determines an object's type by inspection of its method or attribute signature rather than by explicit relationship to some type object ("If it looks like a duck and quacks like a duck, it must be a duck.") By emphasizing interfaces rather than specific types, well-designed code improves its flexibility by allowing polymorphic substitution. Duck-typing avoids tests using type() or isinstance(). Instead, it typically employs the EAFP (Easier to Ask Forgiveness than Permission) style of programming.

    ...

    try:
       _ = (e for e in my_object)
    except TypeError:
       print(my_object, 'is not iterable')
    
  3. The collections module provides some abstract base classes, which allow to ask classes or instances if they provide particular functionality, for example:

    from collections.abc import Iterable
    
    if isinstance(e, Iterable):
        # e is iterable
    

    However, this does not check for classes that are iterable through __getitem__.

wjandrea
  • 28,235
  • 9
  • 60
  • 81
miku
  • 181,842
  • 47
  • 306
  • 310
  • 41
    `[e for e in my_object]` can raise an exception for other reasons, ie `my_object` is undefined or possible bugs in `my_object` implementation. – Nick Dandoulakis Dec 23 '09 at 12:39
  • 41
    A string *is* a sequence (`isinstance('', Sequence) == True`) *and* as any sequence it *is* iterable (`isinstance('', Iterable)`). Though `hasattr('', '__iter__') == False` and it might be confusing. – jfs Dec 24 '09 at 00:11
  • 91
    If `my_object` is very large (say, infinite like `itertools.count()`) your list comprehension will take up a lot of time/memory. Better to make a generator, which will never try to build a (potentially infinite) list. – Chris Lutz Dec 24 '09 at 03:42
  • 17
    What if *some_object* throws TypeError caused by other reason(bugs etc.) too? How can we tell it from the "Not iterable TypeError"? – Shaung Sep 13 '11 at 07:34
  • You can look at the error message: except TypeError as e: if "is not iterable" in e: print my_object, 'is not iterable'; else: raise – Arne Babenhauserheide Jun 22 '12 at 10:01
  • @Shaung This remark was already made by Nick in the first comment. – Piotr Dobrogost Sep 23 '12 at 20:49
  • 1
    Another case where isinstance falls down is for some C++ iterable classes – Jeremy Cowles Oct 24 '12 at 20:49
  • @JeremyCowles: could you elaborate? is there no way to connect to C++ classes in such a way that they behave "correctly" with `isinstance(e, Iterable)`? And why do you say it's "another case"? Do you mean old-style classes? – max Dec 15 '12 at 19:58
  • 3
    @max Adding check for `TypeError` doesn't solve the problem described by Nick thus Shaung's comment in a sense just reiterates Nick's comment. Checking description of an exception as Arne's suggested is always a bad idea as this description is implementation detail. – Piotr Dobrogost Dec 16 '12 at 22:15
  • 2
    @PiotrDobrogost Yes, you're right. `TypeError` may be raised from deeper layers, and an exception message may change in the next version. I guess that's why I only feel comfortable with EAFP approach when the behavior I desire is the same regardless of what caused the exception. – max Dec 16 '12 at 22:36
  • @ChrisLutz, changed the list comprehension to a generator expression, that should at least help performance. – miku Oct 16 '13 at 17:15
  • 65
    Note that in Python 3: `hasattr(u"hello", '__iter__')` returns `True` – Carlos Apr 21 '14 at 02:27
  • An interesting test case: `class X(object): __iter__ = 1`. 3 actually doesn't work with this. – jpmc26 Jun 19 '14 at 22:53
  • 1
    @Stiivi *Custom classes which implement `__iter__` are automatically subclasses of `collections.Iterable`* Could you please provide reference for this statement? – Piotr Dobrogost May 01 '15 at 14:22
  • 1
    only iterables have a `len()` right? so wouldn't a simple `try: l = len(myObj)` be enough to determine if `myObj` is an iterable? – joH1 Nov 20 '16 at 17:07
  • 4
    A side effect of checking if an object is iterable by iterating on it as per 2. is that you will consume the first item of a generator/iterator which can have some undesirable consequences. – Kel Solaar Jul 21 '17 at 11:29
  • 4
    (2) is just a horrible and unintuitive version of (1). I actually didn't expect that to work correctly until I tried it. It's not obvious that creating a generator will immediately throw an exception if the object isn't iterable. I think the answer would be better if that was removed. – Aran-Fey Apr 20 '18 at 22:13
  • 1
    `xrange` is one object which has no `__iter__` method and is not a sequence, yet it is iterable. – haridsv Sep 24 '18 at 10:33
  • @KelSolaar has already pointed out that consuming an element can have side effects, but even trying to call iter() on an object is bad, as it can be an expensive operation. For example, in Pytorch, constructing an iterator over a DataLoader involves spawning a multitude of loading processes. – szali Jul 25 '19 at 10:03
  • @Carlos Why wouldn't it? A unicode string is perfectly iterable, right? – Mast Jul 30 '19 at 11:40
  • 1
    There is another case. Take an object that you want to provide a `__getitem__` for, but you do not want to allow iteration. To force this, you can implement `__iter__`, but an error. In this case, `isinstance(x, Iterable)` will actually cause a false positive. This may seem academic, but in PySpark, there is a `Column` object implemented in exactly this way. – John Haberstroh Jun 21 '23 at 03:11
697

Duck typing

try:
    iterator = iter(the_element)
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(the_element, 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).

Neuron
  • 5,141
  • 5
  • 38
  • 59
Georg Schölly
  • 124,188
  • 49
  • 220
  • 267
  • 54
    From "Fluent Python" by Luciano Ramalho: As of Python 3.4, the most accurate way to check whether an object x is iterable is to call iter(x) and handle a TypeError exception if it isn’t. This is more accurate than using isinstance(x, abc.Iterable), because iter(x) also considers the legacy __getitem__ method, while the Iterable ABC does not. – Yurkol Jun 11 '17 at 07:11
  • In case you're thinking "oh I'll just `isinstance(x, (collections.Iterable, collections.Sequence))` instead of `iter(x)`", note that this still won't detect an iterable object that implements only `__getitem__` but not `__len__`. Use `iter(x)` and catch the exception. – Dale May 12 '18 at 19:03
  • Your second answer doesn't work. In PyUNO if I do `iter(slide1)`, all goes well, however the `isinstance(slide1, Iterable)` throws `TypeError: issubclass() arg 1 must be a class`. – Hi-Angel Jul 20 '19 at 17:08
  • 1
    @Hi-Angel sounds like a bug in `PyUNO` Notice that your error message says `issubclass()` instead of `isinstance()`. – Georg Schölly Jul 23 '19 at 05:28
  • @GeorgSchölly well, although it's sad that PyUNO is not as good as it could be *(it certainly has problems, e.g. not exposing classes of exceptions; even, almost none of existing classes)*, however what this says to me is that `isinstance()` is unreliable. You can't take a random python library, and be sure that it doesn't have a "bug" too. – Hi-Angel Jul 23 '19 at 06:36
  • 4
    Calling iter() over an object can be an expensive operation (see DataLoader in Pytorch, which forks/spawns multiple processes on iter()). – szali Jul 25 '19 at 10:04
  • 2
    It seems that *enumerate()* has the same effect as iter() (Python 3), which may simplify things a bit if the next thing you want to do is enumerating the sequence - no need for explicit iter() beforehand, as enumerate() will raise an appropriate exception by itself if necessary. – Marcin Wojnarski Nov 22 '20 at 21:57
  • I haven't tested this, but I suspect iterating the object could be problematic (ie. pure vs impure actions) for instance -- I wouldn't want to iterate a generator to see if it's iterable – Schalton Jan 29 '21 at 17:25
  • @szali that's really just bad design on PyTorch's side then. – juanpa.arrivillaga Aug 25 '21 at 19:07
  • @MarcinWojnarski well, *anything* that does iteration will eventually raise that error – juanpa.arrivillaga Aug 25 '21 at 19:07
216

I'd like to shed a little bit more light on the interplay of iter, __iter__ and __getitem__ and what happens behind the curtains. Armed with that knowledge, you will be able to understand why the best you can do is

try:
    iter(maybe_iterable)
    print('iteration will probably work')
except TypeError:
    print('not iterable')

I will list the facts first and then follow up with a quick reminder of what happens when you employ a for loop in python, followed by a discussion to illustrate the facts.

Facts

  1. You can get an iterator from any object o by calling iter(o) if at least one of the following conditions holds true:

    a) o has an __iter__ method which returns an iterator object. An iterator is any object with an __iter__ and a __next__ (Python 2: next) method.

    b) o has a __getitem__ method.

  2. Checking for an instance of Iterable or Sequence, or checking for the attribute __iter__ is not enough.

  3. If an object o implements only __getitem__, but not __iter__, iter(o) will construct an iterator that tries to fetch items from o by integer index, starting at index 0. The iterator will catch any IndexError (but no other errors) that is raised and then raises StopIteration itself.

  4. In the most general sense, there's no way to check whether the iterator returned by iter is sane other than to try it out.

  5. If an object o implements __iter__, the iter function will make sure that the object returned by __iter__ is an iterator. There is no sanity check if an object only implements __getitem__.

  6. __iter__ wins. If an object o implements both __iter__ and __getitem__, iter(o) will call __iter__.

  7. If you want to make your own objects iterable, always implement the __iter__ method.

for loops

In order to follow along, you need an understanding of what happens when you employ a for loop in Python. Feel free to skip right to the next section if you already know.

When you use for item in o for some iterable object o, Python calls iter(o) and expects an iterator object as the return value. An iterator is any object which implements a __next__ (or next in Python 2) method and an __iter__ method.

By convention, the __iter__ method of an iterator should return the object itself (i.e. return self). Python then calls next on the iterator until StopIteration is raised. All of this happens implicitly, but the following demonstration makes it visible:

import random

class DemoIterable(object):
    def __iter__(self):
        print('__iter__ called')
        return DemoIterator()

class DemoIterator(object):
    def __iter__(self):
        return self

    def __next__(self):
        print('__next__ called')
        r = random.randint(1, 10)
        if r == 5:
            print('raising StopIteration')
            raise StopIteration
        return r

Iteration over a DemoIterable:

>>> di = DemoIterable()
>>> for x in di:
...     print(x)
...
__iter__ called
__next__ called
9
__next__ called
8
__next__ called
10
__next__ called
3
__next__ called
10
__next__ called
raising StopIteration

Discussion and illustrations

On point 1 and 2: getting an iterator and unreliable checks

Consider the following class:

class BasicIterable(object):
    def __getitem__(self, item):
        if item == 3:
            raise IndexError
        return item

Calling iter with an instance of BasicIterable will return an iterator without any problems because BasicIterable implements __getitem__.

>>> b = BasicIterable()
>>> iter(b)
<iterator object at 0x7f1ab216e320>

However, it is important to note that b does not have the __iter__ attribute and is not considered an instance of Iterable or Sequence:

>>> from collections import Iterable, Sequence
>>> hasattr(b, '__iter__')
False
>>> isinstance(b, Iterable)
False
>>> isinstance(b, Sequence)
False

This is why Fluent Python by Luciano Ramalho recommends calling iter and handling the potential TypeError as the most accurate way to check whether an object is iterable. Quoting directly from the book:

As of Python 3.4, the most accurate way to check whether an object x is iterable is to call iter(x) and handle a TypeError exception if it isn’t. This is more accurate than using isinstance(x, abc.Iterable) , because iter(x) also considers the legacy __getitem__ method, while the Iterable ABC does not.

On point 3: Iterating over objects which only provide __getitem__, but not __iter__

Iterating over an instance of BasicIterable works as expected: Python constructs an iterator that tries to fetch items by index, starting at zero, until an IndexError is raised. The demo object's __getitem__ method simply returns the item which was supplied as the argument to __getitem__(self, item) by the iterator returned by iter.

>>> b = BasicIterable()
>>> it = iter(b)
>>> next(it)
0
>>> next(it)
1
>>> next(it)
2
>>> next(it)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

Note that the iterator raises StopIteration when it cannot return the next item and that the IndexError which is raised for item == 3 is handled internally. This is why looping over a BasicIterable with a for loop works as expected:

>>> for x in b:
...     print(x)
...
0
1
2

Here's another example in order to drive home the concept of how the iterator returned by iter tries to access items by index. WrappedDict does not inherit from dict, which means instances won't have an __iter__ method.

class WrappedDict(object): # note: no inheritance from dict!
    def __init__(self, dic):
        self._dict = dic

    def __getitem__(self, item):
        try:
            return self._dict[item] # delegate to dict.__getitem__
        except KeyError:
            raise IndexError

Note that calls to __getitem__ are delegated to dict.__getitem__ for which the square bracket notation is simply a shorthand.

>>> w = WrappedDict({-1: 'not printed',
...                   0: 'hi', 1: 'StackOverflow', 2: '!',
...                   4: 'not printed', 
...                   'x': 'not printed'})
>>> for x in w:
...     print(x)
... 
hi
StackOverflow
!

On point 4 and 5: iter checks for an iterator when it calls __iter__:

When iter(o) is called for an object o, iter will make sure that the return value of __iter__, if the method is present, is an iterator. This means that the returned object must implement __next__ (or next in Python 2) and __iter__. iter cannot perform any sanity checks for objects which only provide __getitem__, because it has no way to check whether the items of the object are accessible by integer index.

class FailIterIterable(object):
    def __iter__(self):
        return object() # not an iterator

class FailGetitemIterable(object):
    def __getitem__(self, item):
        raise Exception

Note that constructing an iterator from FailIterIterable instances fails immediately, while constructing an iterator from FailGetItemIterable succeeds, but will throw an Exception on the first call to __next__.

>>> fii = FailIterIterable()
>>> iter(fii)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: iter() returned non-iterator of type 'object'
>>>
>>> fgi = FailGetitemIterable()
>>> it = iter(fgi)
>>> next(it)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/path/iterdemo.py", line 42, in __getitem__
    raise Exception
Exception

On point 6: __iter__ wins

This one is straightforward. If an object implements __iter__ and __getitem__, iter will call __iter__. Consider the following class

class IterWinsDemo(object):
    def __iter__(self):
        return iter(['__iter__', 'wins'])

    def __getitem__(self, item):
        return ['__getitem__', 'wins'][item]

and the output when looping over an instance:

>>> iwd = IterWinsDemo()
>>> for x in iwd:
...     print(x)
...
__iter__
wins

On point 7: your iterable classes should implement __iter__

You might ask yourself why most builtin sequences like list implement an __iter__ method when __getitem__ would be sufficient.

class WrappedList(object): # note: no inheritance from list!
    def __init__(self, lst):
        self._list = lst

    def __getitem__(self, item):
        return self._list[item]

After all, iteration over instances of the class above, which delegates calls to __getitem__ to list.__getitem__ (using the square bracket notation), will work fine:

>>> wl = WrappedList(['A', 'B', 'C'])
>>> for x in wl:
...     print(x)
... 
A
B
C

The reasons your custom iterables should implement __iter__ are as follows:

  1. If you implement __iter__, instances will be considered iterables, and isinstance(o, collections.abc.Iterable) will return True.
  2. If the object returned by __iter__ is not an iterator, iter will fail immediately and raise a TypeError.
  3. The special handling of __getitem__ exists for backwards compatibility reasons. Quoting again from Fluent Python:

That is why any Python sequence is iterable: they all implement __getitem__ . In fact, the standard sequences also implement __iter__, and yours should too, because the special handling of __getitem__ exists for backward compatibility reasons and may be gone in the future (although it is not deprecated as I write this).

timgeb
  • 76,762
  • 20
  • 123
  • 145
  • so it is safe to define a predicate `is_iterable` by returning `True` in the `try` block and `False` in the `except TypeError` block? – alancalvitti May 01 '19 at 15:35
  • This is a great answer. I think it highlights the unintuitive and unfortunate nature of the getitem protocol. It should never have been added. – Neil G Jul 29 '19 at 02:29
104

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

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.

Pekka Klärck
  • 2,348
  • 2
  • 18
  • 19
  • 1
    Also `typing.Dict` is considered iterable by `iter(Dict)` but `list(Dict)` fails with error `TypeError: Parameters to generic types must be types. Got 0.`. As expected `isinstance(Dict, Iterable)` returns false. – Pekka Klärck Apr 16 '20 at 13:02
  • 3
    I came to the same conclusion, but for different reasons. Using `iter` caused some of our code that used "pre-caching" to slow down unnecessarily. If the `__iter__` code is slow, so will calling `iter`... any time you just want to see if something is iterable. – thorwhalen May 07 '20 at 13:19
  • 3
    Would it be worth to add a note to that last bit, noting that Python 2 isn't actively being supported by the devs anymore, and shouldn't be used for new code if Python 3 is an option? – Gloweye Jul 23 '20 at 20:02
  • Just found a pretty weird corner case in which `isinstance(obj, Iterable)` fails: numpy 'arrays' of individual values. If you have 'obj = np.array(int(1))`, numpy will happily say `obj = array(1)`. The shape is an empty tuple, and `len(obj)` returns `TypeError: len() of unsized object`. HOWEVER! If you write: `isinstance(obj, Iterable)` you get...`True` . Calamity – physincubus Jan 17 '22 at 06:31
46

Since Python 3.5 you can use the typing module from the standard library for type related things:

from typing import Iterable

...

if isinstance(my_item, Iterable):
    print(True)
Rotareti
  • 49,483
  • 23
  • 112
  • 108
  • 1
    This will return `True` for single, string objects fyi. – waydegg Jan 30 '21 at 22:49
  • 4
    @waydegg Yes, strings are iterable. – Rotareti Jan 31 '21 at 11:35
  • 6
    In Python **3.6** this code doesn't work. In **3.7** it does work. It looks like it will be [deprecated](https://www.python.org/dev/peps/pep-0585/) in **3.9**. `typing` is for type-checking tools (e.g. **MyPy**, **PyCharm**) and doesn't guarantee this behaviour. I think you meant to import the `Iterable` class from `collections.abc` instead. – c z Feb 26 '21 at 10:19
32

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

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
jldupont
  • 93,734
  • 56
  • 203
  • 318
  • 11
    "duck typing" I believe? :) – willem Dec 23 '09 at 12:25
  • 10
    @willem: or "don't ask for permission but for forgiveness" ;-) – jldupont Dec 23 '09 at 12:29
  • 14
    @willem Both "permission" and "forgiveness" styles qualify as duck typing. If you ask what an object can *do* rather than what it *is*, that's duck typing. If you use introspection, that's "permission"; if you just try to do it and see if it works or not, that's "forgiveness". – Mark Reed Nov 12 '13 at 01:44
  • 1
    more about *don't ask what Python can do for you, ask what you can do for Python to work* – mins Jun 14 '21 at 13:39
23

In Python <= 2.5, you can't and shouldn't - iterable was an "informal" interface.

But since Python 2.6 and 3.0 you can leverage the new ABC (abstract base class) infrastructure along with some builtin ABCs which are available in the collections module:

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)

Now, whether this is desirable or actually works, is just a matter of conventions. As you can see, you can register a non-iterable object as Iterable - and it will raise an exception at runtime. Hence, isinstance acquires a "new" meaning - it just checks for "declared" type compatibility, which is a good way to go in Python.

On the other hand, if your object does not satisfy the interface you need, what are you going to do? Take the following example:

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

If the object doesn't satisfy what you expect, you just throw a TypeError, but if the proper ABC has been registered, your check is unuseful. On the contrary, if the __iter__ method is available Python will automatically recognize object of that class as being Iterable.

So, if you just expect an iterable, iterate over it and forget it. On the other hand, if you need to do different things depending on input type, you might find the ABC infrastructure pretty useful.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Alan Franzoni
  • 3,041
  • 1
  • 23
  • 35
  • 14
    don't use bare `except:` in the example code for beginners. It promotes bad practice. – jfs Dec 23 '09 at 23:59
  • J.F.S: I wouldn't, but I needed to go through multiple exception-raising code and I didn't want to catch the specific exception... I think the purpose of this code is pretty clear. – Alan Franzoni Dec 24 '09 at 17:17
21
try:
  #treat object as iterable
except TypeError, e:
  #object is not actually iterable

Don't run checks to see if your duck really is a duck to see if it is iterable or not, treat it as if it was and complain if it wasn't.

badp
  • 11,409
  • 3
  • 61
  • 89
  • 3
    Technically, during iteration your computation might throw a `TypeError` and throw you off here, but basically yes. – Chris Lutz Dec 23 '09 at 12:22
  • I know in .NET it was a bad idea to have exceptions handle program flow, as exceptions were **slow**. How quickly does python handle exceptions? – willem Dec 23 '09 at 12:26
  • 6
    @willem: Please use timeit to perform a benchmark. Python exceptions are often faster than if-statements. They can take a slightly shorter path through the interpreter. – S.Lott Dec 23 '09 at 14:24
  • 2
    @willem: IronPython has slow (compared to CPython) exceptions. – jfs Dec 24 '09 at 00:01
  • 2
    A working try: statement is really fast. So if you have few exceptions, try-except is fast. If you expect many exceptions, “if” can be faster. – Arne Babenhauserheide Jun 22 '12 at 10:04
  • 2
    Shouldn't the exception object be caught by adding "`as e`" after `TypeError` instead of by adding "`, e`"? – HelloGoodbye Dec 12 '16 at 12:53
  • here's a good rule of thumb, `try` is faster than `if`, but `except` is slower than `else`. exception lookup is generally slow regardless of what language you're using. Note that this doesn't include execution between `elif` and `finally`, where a blank `try`/`finally` is generally faster than a blank `if True`/`else`. but **be smart and use this stuff wizely**, `finally` is generally used for context management rather than basic logic and shouldn't be substituted due to what it ensures. – Tcll Aug 14 '18 at 15:03
  • corporate slave coding –  Nov 30 '21 at 10:18
18

You could try this:

def iterable(a):
    try:
        (x for x in a)
        return True
    except TypeError:
        return False

If we can make a generator that iterates over it (but never use the generator so it doesn't take up space), it's iterable. Seems like a "duh" kind of thing. Why do you need to determine if a variable is iterable in the first place?

Chris Lutz
  • 73,191
  • 16
  • 130
  • 183
  • What about `iterable(itertools.repeat(0))`? :) – badp Dec 23 '09 at 12:24
  • 7
    @badp, the `(x for x in a)` just creates a generator, it doesn't do any iteration on a. – catchmeifyoutry Dec 23 '09 at 12:31
  • 5
    Is trying `(x for x in a)` precisely equivalent to trying `iterator = iter(a)`? Or there are some cases where the two are different? – max Dec 15 '12 at 20:05
  • Isn't `for _ in a: break` more straightforward ? Is it slower ? – Mr_and_Mrs_D Oct 23 '15 at 22:16
  • 3
    @Mr_and_Mrs_D that's bad if the tested object is an iterator that's iterated over afterwards (it will be 1 item short since it's position can't be reset), creating garbage generators doesn't iterate over the object as they're not iterated over, though I'm not certain that it will 100% raise a TypeError if not iterable. – Tcll Aug 14 '18 at 14:40
15

The best solution I've found so far:

hasattr(obj, '__contains__')

which basically checks if the object implements the in operator.

Advantages (none of the other solutions has all three):

  • it is an expression (works as a lambda, as opposed to the try...except variant)
  • it is (should be) implemented by all iterables, including strings (as opposed to __iter__)
  • works on any Python >= 2.5

Notes:

  • the Python philosophy of "ask for forgiveness, not permission" doesn't work well when e.g. in a list you have both iterables and non-iterables and you need to treat each element differently according to it's type (treating iterables on try and non-iterables on except would work, but it would look butt-ugly and misleading)
  • solutions to this problem which attempt to actually iterate over the object (e.g. [x for x in obj]) to check if it's iterable may induce significant performance penalties for large iterables (especially if you just need the first few elements of the iterable, for example) and should be avoided
totallyhuman
  • 155
  • 1
  • 8
Vlad
  • 367
  • 2
  • 3
  • 3
    Nice, but why not use the collections module as proposed in http://stackoverflow.com/questions/1952464/in-python-how-do-i-determine-if-a-variable-is-iterable/1952481#1952481? Seems more expressive to me. – Dave Abrahams May 03 '11 at 04:10
  • 1
    It's shorter (and doesn't require additional imports) without losing any clarity: having a "contains" method feels like a natural way to check if something is a collection of objects. – Vlad Nov 25 '11 at 12:04
  • 50
    Just because something can contain something doesn't necessarily mean it's iterable. For example, a user can check if a point is in a 3D cube, but how would you iterate through this object? – Casey Kuball May 18 '12 at 14:52
  • 16
    This is incorrect. An iterable itself does not support __contains__, at least with Python 3.4. – Peter Shinners Jan 21 '15 at 05:39
14

I found a nice solution here:

isiterable = lambda obj: isinstance(obj, basestring) \
    or getattr(obj, '__iter__', False)
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
jbochi
  • 28,816
  • 16
  • 73
  • 90
11

According to the Python 2 Glossary, iterables are

all sequence types (such as list, str, and tuple) and some non-sequence types like dict and file and objects of any classes you define with an __iter__() or __getitem__() method. Iterables can be used in a for loop and in many other places where a sequence is needed (zip(), map(), ...). When an iterable object is passed as an argument to the built-in function iter(), it returns an iterator for the object.

Of course, given the general coding style for Python based on the fact that it's “Easier to ask for forgiveness than permission.”, the general expectation is to use

try:
    for i in object_in_question:
        do_something
except TypeError:
    do_something_for_non_iterable

But if you need to check it explicitly, you can test for an iterable by hasattr(object_in_question, "__iter__") or hasattr(object_in_question, "__getitem__"). You need to check for both, because strs don't have an __iter__ method (at least not in Python 2, in Python 3 they do) and because generator objects don't have a __getitem__ method.

Anaphory
  • 6,045
  • 4
  • 37
  • 68
9

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.

fmonegaglia
  • 2,749
  • 2
  • 24
  • 34
  • 3
    Whenever you do sth like `if x: return True` `else: return False` (with `x` being boolean) you can write this as `return x`. In your case `return isinstance(…)` without any `if`. – Alfe May 07 '13 at 21:34
  • Since you acknowledge that Alfe's solution is better, why didn't you edit your answer to simply say that? Instead, you now have BOTH versions in your answer. Unnecessary verbosity. Submitting an edit to fix this. – ToolmakerSteve Dec 12 '13 at 23:21
  • 2
    You should catch "TypeError" in the ` except: return False ` line. Catching everything is a bad pattern. – Mariusz Jamro Jul 03 '14 at 07:51
  • Know that. I translated that piece of code from the NumPy library, which uses the generic exception. – fmonegaglia Aug 25 '14 at 13:51
  • Just because a code is taken from NumPy doesn't mean it's good... pattern or not, the only time catching everything should be done is if you're explicitly error handling inside your program. – Tcll Aug 14 '18 at 15:49
5

has a built-in function like that:

from pandas.util.testing import isiterable
MSeifert
  • 145,886
  • 38
  • 333
  • 352
Soerendip
  • 7,684
  • 15
  • 61
  • 128
  • this however just looks whether there is `__iter__` and not really cares about sequences and similar. – ead Mar 03 '20 at 08:02
4

It's always eluded me as to why python has callable(obj) -> bool but not iterable(obj) -> bool...
surely it's easier to do hasattr(obj,'__call__') even if it is slower.

Since just about every other answer recommends using try/except TypeError, where testing for exceptions is generally considered bad practice among any language, here's an implementation of iterable(obj) -> bool I've grown more fond of and use often:

For python 2's sake, I'll use a lambda just for that extra performance boost...
(in python 3 it doesn't matter what you use for defining the function, def has roughly the same speed as lambda)

iterable = lambda obj: hasattr(obj,'__iter__') or hasattr(obj,'__getitem__')

Note that this function executes faster for objects with __iter__ since it doesn't test for __getitem__.

Most iterable objects should rely on __iter__ where special-case objects fall back to __getitem__, though either is required for an object to be iterable.
(and since this is standard, it affects C objects as well)

Tcll
  • 7,140
  • 1
  • 20
  • 23
  • he doesn't provide working code, let alone talk about python performance... although this answer was really just for convenience like I've seen done numerous times on here. – Tcll Mar 22 '19 at 01:23
  • "where testing for exceptions is generally considered bad practice among any language" Python is not included in your generalization. Python handles exceptions very efficiently, and idiomatic Python necessitates the use of try...except blocks as part of its duck typing philosophy as mentioned in numerous answers to this question. – Bobort May 22 '23 at 16:28
  • "Python handles exceptions very efficiently" I'm afraid I have to heavily disagree, `except` is one of the slowest, most inefficient, keywords in python, it has it's purpose, but should not be used everywhere, unless you want the lowest performing code imaginable, given `except` is actually invoked... `try` is faster than `if`, but `except` is much slower than `else` due to exception lookups. – Tcll May 23 '23 at 00:03
  • There is so much literature on the EAFP philosophy employed by Python, even on StackOverflow. Python, when compared to other languages such as C++, handles exceptions more efficiently because it is a higher level language. It's science--the algorithm is cheaper because of the way that Python processes the code. I liked the answer in this question: https://stackoverflow.com/questions/1835756/using-try-vs-if-in-python – Bobort May 24 '23 at 02:52
  • "Python, when compared to other languages such as C++, handles exceptions more efficiently because it is a higher level language" efficiency has nothing to do with being higher level, it has everything to do with how the CPU ultimately executes the bytecode/machine-code, which ultimately yes, python is more efficient since exceptions are PyObject instances in C, where as exceptions in C++ are something completely different, but that still doesn't change the fact that `except` is the most inefficient keyword in python, and makes your execution incredibly slow if invoked often. – Tcll May 24 '23 at 19:36
  • Let's rewind. It sounds like you are trying to encourage Python programmers to use conditions to check for potential errors rather than to use the idiomatic way with `try...except` blocks. Please clarify your position on that matter. – Bobort May 25 '23 at 21:22
  • Indeed I am, the conditional approach is generally faster, since `except` is horribly inefficient due to exception lookups, however again, there are places where `except` should be used where it won't be invoked often, such as within a run loop where it's known the execution will only fail 5% or so of the time, as this ensures runtime stability. However, `except` should NOT be used as a substitute for conditional testing, where the especially where the failure rate is above ~30% as this will greatly slow down your runtime. – Tcll May 26 '23 at 03:23
3
def is_iterable(x):
    try:
        0 in x
    except TypeError:
        return False
    else:
        return True

This will say yes to all manner of iterable objects, but it will say no to strings in Python 2. (That's what I want for example when a recursive function could take a string or a container of strings. In that situation, asking forgiveness may lead to obfuscode, and it's better to ask permission first.)

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)

Many other strategies here will say yes to strings. Use them if that's what you want.

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__')

Note: is_iterable() will say yes to strings of type bytes and bytearray.

  • bytes objects in Python 3 are iterable True == is_iterable(b"string") == is_iterable("string".encode('utf-8')) There is no such type in Python 2.
  • bytearray objects in Python 2 and 3 are iterable True == is_iterable(bytearray(b"abc"))

The O.P. hasattr(x, '__iter__') approach will say yes to strings in Python 3 and no in Python 2 (no matter whether '' or b'' or u''). Thanks to @LuisMasuelli for noticing it will also let you down on a buggy __iter__.

Bob Stein
  • 16,271
  • 10
  • 88
  • 101
3

There are a lot of ways to check if an object is iterable:

from collections.abc import Iterable
myobject = 'Roster'
  
if isinstance(myobject , Iterable):
    print(f"{myobject } is iterable") 
else:
   print(f"strong text{myobject } is not iterable")
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
2

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Luis Masuelli
  • 12,079
  • 10
  • 49
  • 87
2

The isiterable func at the following code returns True if object is iterable. if it's not iterable returns False

def isiterable(object_):
    return hasattr(type(object_), "__iter__")

example

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
Nomad
  • 918
  • 6
  • 23
2

In my code I used to check for non iterable objects:

hasattr(myobject,'__trunc__')

This is quite quick and can be used to check for iterables too (use not).

I'm not 100% sure if this solution works for all objects, maybe other can give a some more background on it. __trunc__ method seams to be related to numerical types (all objects that can be rounded to integers needs it). But I didn't found any object that contains __trunc__ together with __iter__ or __getitem__.

B.R.
  • 234
  • 2
  • 7
1

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

Community
  • 1
  • 1
DarthCadeus
  • 372
  • 3
  • 13
  • unfortunately not all iterable objects use `__len__`... for this case, it's usually the improper use of calculating distance between 2 objects. where `obj.dist()` could be easily substituted. – Tcll Aug 14 '18 at 16:32
  • Yeah. Most user defined iterables implement iter and getitem but not len. However, built in types do, and if you want to check if you can call len function on it, checking for len is more secure. But you are right. – DarthCadeus Aug 14 '18 at 23:18
1

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
Jan Musil
  • 508
  • 5
  • 15
0

Kinda late to the party but I asked myself this question and saw this then thought of an answer. I don't know if someone already posted this. But essentially, I've noticed that all iterable types have __getitem__() in their dict. This is how you would check if an object was an iterable without even trying. (Pun intended)

def is_attr(arg):
    return '__getitem__' in dir(arg)
Raymond Hettinger
  • 216,523
  • 63
  • 388
  • 485
lakam99
  • 575
  • 4
  • 9
  • 24
-1

Maybe just write hasattr(obj, "__iter__")

Or... something like this might work:

def is_iterable(obj: object) -> bool:
    return hasattr(obj, "__iter__")  
Toothpick Anemone
  • 4,290
  • 2
  • 20
  • 42