How to check whether an object is iterable in Python ?
Posted on Sat 07 September 2019 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 ofcollections.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:
-
Checks whether the object implements
__iter__
, and calls that to obtain an iterator. -
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). -
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!