How to check whether an object is iterable in Python ?

Iterable vs Iterator

In simple words, any object that could be looped over is an iterable. For instance, a list object is an iterable and so is a str object.

>>> numbers = [1, 2, 3, 4] 
>>> for number in numbers: 
        print(number, end=" ") 
1 2 3 4 

>>> name = "Guido"
>>> for letter in name: 
        print(letter, end=" ") 
G u i d o

The list numbers and string name are iterables, because we are able to loop over them (using a for-loop in this case).

However, an int object is a not an iterable and so is a float object.

>>> number = 5    
>>> for i in number: 
    print(i) 
TypeError: 'int' object is not iterable
>>>

The integer object number is not an iterable, as we are not able to loop over it.

An iterator is an object that enables Python to loop over an iterable. Whenever Python loops over an iterable, it asks the iterator of the iterable to give the next element in the iterable, until the iterable is exhausted.

Often, the iterators are created implicitly. In the above examples, when we used the for-loop on the iterables, Python created an iterator from them and then looped over them, behind the scenes.

Later in this post, we will see how to obtain an iterator from an iterable and use it loop over the iterable.

A Book/Bookmark analogy

We could consider an iterable to be a book, in which each page in the book is analogous to the elements in the iterable.

Bookmark is analogous to iterator, as it helps us to turn the pages just like an iterator helps us to loop over an iterable.

A more technical definition

iterable: Objects implementing __iter__ method(returning an iterator) are iterables and so are objects implementing __getitem__method (that takes 0-based indexes).

Making an instance of a user-defined class iterable

We could make an object of a user defined class iterable either by implementing the __iter__ method or the __getitem__ method.

>>> from collections import namedtuple
>>> Point = namedtuple('Point', 'x y')
>>> class Graph:
...     def __init__(self, points):
...         self._points = points
...         
...     def __getitem__(self, position):    
...         return self._points[position]
...     
>> g = Graph([Point(0, 0), Point(1, 2), Point(4, 3)])
>>> for point in g:
       print(point)

Point(x=0, y=0)
Point(x=1, y=2)
Point(x=4, y=3)

In the above example, I have implemented a user defined class Graph. It has a __init__ method which stores a list of Point objects. It also has a __getitem__method which returns a Point object at a certain index. An instance of the Graph class, g has suddenly become iterable by virtue of the __getitem__ method.

I could replace the __getitem__ with the __iter__ method to return an iterator.

>>> class Graph:
...     def __init__(self, points):
...         self._points = points
...         
...     def __iter__(self):    
...         return self._points.__iter__()
...     
>>> g = Graph([Point(0, 0), Point(1, 2), Point(4, 3)])
>>> for point in g:
...     print(point)
...     
Point(x=0, y=0)
Point(x=1, y=2)
Point(x=4, y=3)

Observe that the instance g is still iterable because of the __iter__ method. A __iter__ method should return an iterator. In our __iter__ method, I delegated that job to the __iter__ method available in the builtin list object (self._points is a list).

Iterator: Objects implementing the __next__ no-argument method that returns the next item in a series or raises StopIteration when there are no more items are iterators.

Hopefully, you now understand what iterables and iterators are. Now I will show how to check whether a given object is an iterable or not.

Checking an object’s iterability

We are going to explore the different way of checking whether an object is iterable or not.

  • Using __iter__ method check
>>> name = 'Guido'
>>> if hasattr(name, '__iter__'):
...     print(f'{name} is iterable')
... else:    
...     print(f'{name} is not iterable')
...       
Guido is iterable

We use the hasattr() function to test whether the string object name has __iter__ attribute for checking iterability. However, this check is not comprehensive. In the above example, it worked as expected, since I used Python 3. In Python 2, this check fails our expectation as shown below:

Python 2.7.14 (default, Jul 13 2018, 12:11:36) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-28)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> name = 'Guido'
>>> if hasattr(name, '__iter__'):
...    print("{} is iterable".format(name))
... else:
...    print("{} is not iterable".format(name))                                                                            
... 
Guido is not iterable

Observe that the string object is reported as not iterable by the hasattr() check, although the string is iterable.

  • Using the Iterable class of collections.abc module

We could verify that an object is iterable by checking whether it is an instance of the Iterable class.

Python 2.7.14 (default, Jul 13 2018, 12:11:36) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-28)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> name = 'Guido'
>>> from collections import Iterable
>>> if isinstance(name, Iterable):
...   print("{} is iterable".format(name))
... else:
...   print("{} is not iterable".format(name))
... 
Guido is iterable

The isinstance check reports the string object as iterable correctly using the Iterable class. This works for Python 3 as well. For Python 3.3 and above, you will have to import the Iterable class from collections.abc and not from collections like so:

>>> name = 'Guido'
>>> from collections.abc import Iterable
>>> if isinstance(name, Iterable):
...     print(f"{name} is iterable")
... else:    
...     print(f"{name} is not iterable")
...     
Guido is iterable

However, this check does not work for objects that are iterables by virtue of implementing the __getitem__ method (we discussed this concept above).
Let’s confirm this using the Graph class we implemented earlier which has the __getitem__ method.

>>> class Graph:
...     def __init__(self, points):
...         self._points = points
...         
...     def __getitem__(self, poistion):
...         return self._points[position]
...     
>>> g = Graph([Point(0,0), Point(1, 2), Point(4, 3)])
>>> if isinstance(g, Iterable):
...     print("The instance is an iterable")
... else:
...     print("The instance is not an iterable")
...     
The instance is not an iterable

We see that, the check reports that g is not iterable, although g is actually an iterable. As it turns out, the isinstance(obj, Iterable) check does not detect iterables that iterate with the __getitem__ method(https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable)

So we need to a better mechanism to check iterability – the iter() builtin function to the rescue.

  • Using the iter() builtin function

The iter built-in function works in the following way:

  1. Checks whether the object implements __iter__, and calls that to obtain an iterator.

  2. If __iter__ is not implemented, but __getitem__ is implemented, Python creates an iterator that attempts to fetch items in order, starting from index 0 (zero).

  3. If that fails, Python raises TypeError, usually saying “C object is not iterable,” where C is the class of the target object.

So we could use the exception handler to check if an object is iterable like so:

>>> class Graph:
...     def __init__(self, points):
...         self._points = points
...         
...     def __getitem__(self, poistion):
...         return self._points[position]
...     
>>> g = Graph([Point(0,0), Point(1, 2), Point(4, 3)])
>>> try:
...     iter(g)
...     print("The instance is iterable")
... except TypeError:    
...     print("The instance is not an iterable")
...     
The instance is iterable


>>> name = "Guido"
>>> try:
...     iter(name)
...     print(f"{name} is iterable")
... except TypeError:
...     print(f"{name} is not iterable")
...     
Guido is iterable


>>> number = 5
>>> try:
...     iter(number)
...     print(f"{number} is iterable")
... except TypeError:
...     print(f"{number} is not iterable")
...     
5 is not iterable


Python 2.7.14 (default, Jul 13 2018, 12:11:36) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-28)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> name = "Guido"
>>> try:
...   iter(name)
...   print("{} is iterable".format(name))    
... except TypeError:
...   print("{} is not iterable".format(name))
... 
Guido is iterable

From the above examples, it is clear that iter() function could be used to check the iterability of any object comprehensively. It works as expected for all objects including Python 2's builtin iterable types and iterables that iterate using __getitem__ method.

We could use the iter() function on any iterable to get an iterator and then use the builtin next() method to loop over like so:

>>>> name = "Guido"
>>> str_iterator = iter(name)
>>> str_iterator
<str_iterator at 0x7f507106fdd8>
>>> next(str_iterator)
'G'
>>> next(str_iterator)
'u'
>>> next(str_iterator)
'i'
>>> 
>>> next(str_iterator)
'd'
>>> next(str_iterator)
'o'
>>> next(str_iterator)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

Conclusion

In this blog post, I briefly explained the difference between an iterable and an iterator. I have also illustrated the different ways to check the iterability of an object.

Although __iter__ method and Iterable class could be used, they are not as comprehensive as the iter() method. The only reliable way to determine whether an object is iterable is to call iter(obj).

I hope you now have an answer for the question: “How to check if an object is iterable?”`.

That’s it readers, until next time! Happy coding Python!

links

social