Today we’ll be talking about the multiple iterator and the single iterators.
Here’s the video version:
If you don’t feel comfortable about iterables and iterators, I have an article dedicated to just that topic, so don’t hesitate to read it first.
So, we know what iterables are and that they return iterators or are iterators themselves. But is it possible for an iterable to return more than one iterator? Well, it depends. Some iterables support multiple iterators, others don’t.
Iterables That Support the Multiple Iterator
Let’s start with a list. A list is an iterable, so let’s try to obtain two iterators from it and test whether they can work independently:
>>> nums = [1, 2, 3, 4, 5, 6]
>>> it1 = iter(nums)
>>> it2 = iter(nums)
>>>
>>> next(it1)
1
>>> next(it1)
2
>>> next(it2)
1
>>> next(it2)
2
>>> next(it2)
3
>>> next(it1)
3
>>> next(it1)
4
As you can see, these two iterations are independent. The elements are returned independently by each iterator. So, lists do support multiple iterators.
In the article about iterables and iterators we classified all the iterables as belonging to one of the following categories:
– iterables which are not iterators, but they return an iterator
– iterables which are iterators themselves
A list belongs to the first category. It turns out that all iterables in this category support multiple iterators. And these include: lists, tuples, strings, dictionaries and ranges.
Here are some examples:
– tuple:
>>> animals = ('dog', 'mouse', 'wolf', 'bear')
>>> it1 = iter(animals)
>>> it2 = iter(animals)
>>> next(it1)
'dog'
>>> next(it1)
'mouse'
>>> next(it1)
'wolf'
>>> next(it2)
'dog'
>>> next(it2)
'mouse'
>>> next(it1)
'bear'
>>> next(it1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> next(it2)
'wolf'
>>> next(it2)
'bear'
>>> next(it2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
– string:
>>> word = 'Python'
>>> it1 = iter(word)
>>> it2 = iter(word)
>>> it3 = iter(word)
>>> next(it1), next(it2), next(it3)
('P', 'P', 'P')
>>> next(it1), next(it1), next(it1)
('y', 't', 'h')
>>> next(it2)
'y'
>>> next(it1), next(it1)
('o', 'n')
>>> next(it3)
'y'
>>> next(it1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> next(it2), next(it3)
('t', 't')
– dictionary:
>>> scores = {"Mike": 17, "Luke": 21, "Amanda": 15}
>>> it1 = iter(scores)
>>> it2 = iter(scores)
>>> next(it1)
'Mike'
>>> next(it1)
'Luke'
>>> next(it2)
'Mike'
– range:
>>> nums = range(4, 25, 4)
>>> it1 = iter(nums)
>>> it2 = iter(nums)
>>> next(it1)
4
>>> next(it1)
8
>>> next(it1)
12
>>> next(it1)
16
>>> next(it2)
4
>>> next(it2)
8
>>> next(it2)
12
>>> next(it1)
20
Iterables That Support Single Iterators
On the other hand, iterables which are iterators at the same time, only support single iterators. If we try to define more iterators, they will all reference the same object. This is because the object is the iterator itself. A zip is an example. As it is an iterator, we don’t have to use the iter function, but we can:
>>> z = zip(('a', 'b', 'c'), (1, 2, 3))
>>> it1 = iter(z)
>>> it2 = iter(z)
>>>
>>> next(it1)
('a', 1)
>>> next(it2)
('b', 2)
>>> next(it2)
('c', 3)
So, both iterators are at the same position, so they do not work independently. As we don’t need the iter function, we could rewrite the code as follows:
>>> z = zip(('a', 'b', 'c'), (1, 2, 3))
>>> it1 = z
>>> it2 = z
>>> next(it1)
('a', 1)
>>> next(it2)
('b', 2)
Now, as the zip object is an iterator itself, we can use it in the next function as well:
>>> next(z)
('c', 3)
Apart from zips, this category includes: files, enumerates, maps, zips and filters.
Here are some examples:
– file:
We’ll make use of a text file called workers.txt with the following content:
John Smith, 36, programmer
Emma Jenkins, 38, tester
William Harper, 32, designer
Tom Parker, 44, programmer
The file is saved in the current directory.
>>> f = open("workers.txt")
>>> it1 = iter(f)
>>> it2 = iter(f)
>>> it3 = f # files are iterators themselves
>>> next(it1)
'John Smith, 36, programmer\n'
>>> next(it2)
'Emma Jenkins, 38, tester\n'
>>> next(f)
'William Harper, 32, designer\n'
>>> next(it1)
'Tom Parker, 44, programmer'
>>> next(it2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
– enumerate:
>>> letters = enumerate(['a', 'b', 'c', 'd', 'e'])
>>> it1 = iter(letters)
>>> it2 = iter(letters)
>>> it3 = letters
>>> next(letters)
(0, 'a')
>>> next(it2)
(1, 'b')
>>> next(it2)
(2, 'c')
>>> next(it1)
(3, 'd')
>>> next(letters)
(4, 'e')
– map:
>>> m = map(lambda x: x / 10, (3, 6, 10, 11))
>>> it1 = iter(m)
>>> it2 = m
>>> next(it1)
0.3
>>> next(m)
0.6
>>> next(m)
1.0
>>> next(it2)
1.1
>>> next(it2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
– filter:
>>> digits = filter(str.isdigit, ('I bought 54 pieces 3 days ago.'))
>>> it1 = iter(digits)
>>> it2 = iter(digits)
>>> next(it1)
'5'
>>> next(it1)
'4'
>>> next(it2)
'3'