Iterators¶
Compared to many other programming languages, Python’s syntax is very clear and simple. For intance, see for..in loop below:
numbers = [1, 2, 3, 4, 5]
for number in numbers:
print(number, end=" ")
1 2 3 4 5
But how does the above loop in the code fetches the individual elements from the given list and loop for.
How to use this construct in our own objects if we have to?
Iterator Protocol¶
To create our oun iterator object, we’ll need to implement two dunder(double underscore) methods in the class:
__iter__()
(iter()
): should return iterator object. Used infor
andin
keywords__next__()
(next()
): returns next value orStopIteration
error once all the objects has been looped. Moreover, this method internally usesiter()
method to get an iterator object and the usesnext()
method to iterate over the values.
Let’s have a look at the code below:
numbers = [1, 2, 3]
iterable_numbers = iter(numbers)
print("Calling 1st time: ", next(iterable_numbers))
print("Calling 2nd time: ", next(iterable_numbers))
print("Calling 3rd time: ", next(iterable_numbers))
print("Calling 4th time: ", next(iterable_numbers)) # Raises StopIteration
Calling 1st time: 1
Calling 2nd time: 2
Calling 3rd time: 3
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-2-cdd05496ca3a> in <module>
8 print("Calling 3rd time: ", next(iterable_numbers))
9
---> 10 print("Calling 4th time: ", next(iterable_numbers)) # Raises StopIteration
StopIteration:
Now, lets create our own custom iterator below:
class Increment:
def __init__(self, start, end, step = 1):
self.current = start
self.end = end
self.step = step
def __iter__(self):
"""returns the iterator object"""
return self
def __next__(self):
"""returns the next value or raises StopIteration"""
if self.current > self.end:
raise StopIteration("Stop Iteration")
else:
self.current += self.step
return self.current - self.step
Lets take our sweet custom iterator
Increment
into action:
for i in Increment(0, 10):
print(i, end=", ")
print("")
for i in Increment(0, 20, 2):
print(i, end=", ")
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20,
An iterator object can only be used one. After the
StopIteration
exception has been raised, it will continue raising the same exception.Lets look at the code below for instance,
increment = Increment(0, 3)
print(next(increment))
0
print(next(increment))
1
print(next(increment))
2
print(next(increment))
3
print(next(increment))
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-74-37e11a149c41> in <module>
----> 1 print(next(increment))
<ipython-input-64-be979cba6339> in __next__(self)
10 def __next__(self):
11 if self.current > self.end:
---> 12 raise StopIteration("Stop Iteration")
13 else:
14 self.current += 1
StopIteration: Stop Iteration
print(next(increment)) # Raises StopIteration again
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-80-06d940e8f2eb> in <module>
----> 1 print(next(increment)) # Raises StopIteration again
<ipython-input-64-be979cba6339> in __next__(self)
10 def __next__(self):
11 if self.current > self.end:
---> 12 raise StopIteration("Stop Iteration")
13 else:
14 self.current += 1
StopIteration: Stop Iteration
Using while loop for our
Increment
instead of for…in
increment = Increment(0, 3)
while True:
try:
i = increment.__next__() # or next(increment)
print(i, end=", ")
except StopIteration as error:
print(error, "Raised")
break
0, 1, 2, 3, Stop Iteration Raised