Iterable (iter-able) is an object we can iterate over - we can go over its items one by one.
Python documentation says it is:
An object capable of returning its members one at a time.
It might be:
- an object containing multiple items.
- an object we can split into multiple items.
- an object that generates items.
We could also say it is an object that allows us to read its values one by one.
When an object is iterable, we can:
- Use it in
for
loops. - Use the
in
operator to check if it contains some item. - Use it as an input for comprehensions.
The most practical definition is probably this:
If we can use the object in a for loop, it is iterable.
First, let's look at some built-in iterable objects in Python. Then we'll explain what makes an object iterable and how it works under the bonnet.
Built-in iterable objectsPermalink
CollectionsPermalink
Collections objects are containers that are used to store collections of data. We set what is in the collection when we create it, and for mutable collections, we can also add items to them later.
All collections objects are iterable:
list
(anddeque
)tuple
( andnamedtuple
)set
( andfrozenset
)
# list
my_list = ['one', 'two', 'three']
for i in my_list:
print(i)
# tuple
my_tuple = ('one', 'two', 'three')
for i in my_tuple:
print(i)
# set
my_set = {'one', 'two', 'three'}
for i in my_set:
print(i)
# all 3 above output:
# one
# two
# three
dict
is also iterable, but when we iterate over a dict, we iterate over its keys only:
my_dict = {'a': 1, 'b': 2, 'c': 3}
for i in my_dict:
print(i)
# outputs:
# a
# b
# c
Strings, files and generatorsPermalink
Many other objects are iterable, although their purpose is not to store some items for us. These objects contain data that can be split into smaller parts or generate some data that we can get one by one. We do not put things into these objects, but we can still read their content using iteration.
Probably the most common iterable is str
- we can iterate over its characters:
s = 'hey'
for i in s:
print(i)
# h
# e
# y
Another object which is also iterable is a file object (specifically text file object). When iterating over a text file object, each line in the file is one item.
If we had a file lines.txt like this:
line 1
line 2
line 3
Then it would work like this:
with open('lines.txt') as my_file:
for i in my_file:
print(i)
# outputs:
# line 1
# line 2
# line 3
All generators are also iterable. Generators are objects that yield values.
Say we write a generator function that yields doubles of numbers up to the number provided as an argument:
def doubles(up_to):
number = 0
while number < up_to:
number = number + 1
yield number * 2
We can then iterate over the values it produces:
for x in doubles(3):
print(str(x))
# outputs:
# 2
# 4
# 6
What makes an object iterable?Permalink
To iterate over an iterable, Python first needs to get an iterator (iter-ator) for the iterable.
Iterator is an object that does the actual iteration over an iterable. It feeds data from iterable to the outside world one by one. Iter-ator is used by the for loop
to get items one by one.
Based on this, we can say that what makes an object iterable is the fact that we can get an iter-ator for it.
There is no iterable without an iterator.
Ok, so how do we get an iterator for an iterable?
Getting the iteratorPermalink
To get an iterator for an object, Python uses the built-in function iter()
.
This function takes one argument - an object we want to iterate over.
If the object is iterable, iter()
returns an iter-ator for it. If the object is not iterable, it raises TypeError
.
We pass to iter()
a list of numbers in the following code. We get back a list_iterator
object.
>>> numbers = [1, 2, 3]
>>> iter(numbers)
<list_iterator object at 0x10bf13b20>
The following code passes just the number 5 to the iter()
function. It raises TypeError :
>>> iter(5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
How does iter()
gets an iterator for an object?Permalink
There are two ways iter()
can get an iter-ator for iterable.
-
Iterable has a method
__iter__(self)
.
This method should return an iterator for this iterable. In this case, the iterable itself is responsible for returning its iterator. In Python 3, all built-in iterables we discussed above have the__iter__()
method. -
The second way is that iterable has a method
__getitem__(self, index)
.
This method returns a value for a given index - if there is no such index, it raises an error. So, in this case, iterable is not providing the iterator. Theiter()
function needs to find an iterator for the iterable.
How?
When an object has__getitem__()
method, Python considers it a sequence or map — an object that stores items we can retrieve by their index or key. For these, Python provides several built-in iterators for iteration over sequences, dict and other forms. Soiter()
will use and return one of those built-in itera-tors. Those iterators will use the__getitem__()
method to get items from the iterable, as we'll see later.
(Before Python 3, the __getitem__
was the only way to make things iterable. This way of making objects iterable works mostly for compatibility reasons as all iterables in Python 3 have the __iter__()
method.)
The following image shows how the iter()
function gets an iter-ator for an iterable:
Creating our own iterablePermalink
Now let's have a quick look at how we can create our own iterable by implementing the __getitem__()
method so Python will provide us built-in iterator.
We will look at the __iter__()
method later when we discuss iterators in detail.
Here's a sample implementation of an iterable object that has the __getitem__()
method:
class ZeroToThreeObject:
def __getitem__(self, index):
if index <= 3:
return index * 2
else:
raise IndexError
Our class ZeroToThreeObject
is not very useful, but it is iterable.
In the __getitem__
method, we check if the parameter index is 3 or less
and if so, we return the double of it. Otherwise, we raise IndexError.
Let's now check what happens when we pass our object to the iter()
function:
>>> zero_three = ZeroToThreeObject()
>>> iter(zero_three)
<iterator object at 0x10be21810>
We can see that we got iterator from iter()
function for our object. It works because our object has __getitem__()
method.
So our object is iterable, and we can use it as such:
zero_three = ZeroToThreeObject()
for x in zero_three:
print(str(x))
# outputs:
# 0
# 2
# 4
# 6
It's worth noting that this object is not only iterable but also a sequence (because it has __getitem__
method - as we discussed before), so we can access its items by index too:
zero_three = ZeroToThreeObject()
zero_three[1]
# outputs: 2
zero_three[3]
#outputs: 6
What iterable isn'tPermalink
Iterable is a very useful concept as it abstracts away many details about a particular object. Many functions take an iterable as a parameter without specifying if it should be a list, string, generator etc.
When a function expects iterable, it expects to be able to iterate over it once. If it expects iterable, it can't expect that object will have a length as, e.g. str or list, nor can it expect it will be able to get items from it based on the index or a key as, e.g. list or dict.
- Iterable is not required to have a length - it might not work with the
len()
function. - Iterable is not required to support indexing - we might not be able to get the item from it with index.
- Iterable might even be infinite (usually generators).
The only thing iterable provides is that we can iterate over its items one by one.
SummaryPermalink
An iterable:
- is an object we can iterate over (its values).
- can be used in a for loop to get its items.
- is an object for which we can get an iter-ator.
- There is no iter-able without an iter-ator.
- We can use the
in
operator to check if it contains some value. - A lot of built-in objects are iterables:
list
,set
,dict
,string
,file
etc. - Generators are also iterable (they generate items).
- We can make our objects iterable by providing the
__iter__()
method, which returns an iterator. - Or by providing the
__getitem__(self, index)
method, which returns the value at the given index.
Happy coding!
You might also like
Better Python apps in AWS with stelvio.dev
Deep dives and quick insights into cloud architecture.