Photo by Kelly Sikkema on Unsplash
Iterators
An iterator is an object representing a stream of data; this object returns the data, one element at a time. The iterator objects themselves are required to support the following two methods, which together form the iterator protocol :
.__iter__() : Called to initialize the iterator. It must return an iterator object.
.__next__() : Return the next item from the iterator. If there are no further items, raise the StopIteration
exception.
How to create an Iterator?
We need to implement two methods i.e. .__iter__() and .__next__() to create a custom iterator object:
#example of custom iterator
class SquareIterator:
def __init__(self, sequence):
self._sequence = sequence
self._index = 0
def __iter__(self):
return self
def __next__(self):
if self._index < len(self._sequence):
square = self._sequence[self._index] ** 2
self._index += 1
return square
else:
raise StopIteration
for square in SquareIterator([1, 2, 3, 4, 5]):
print(square)
#output:
1
4
9
16
25
When to use an Iterator in Python?
Iterators come in handy when you need to iterate over a dataset or data stream with an unknown or a huge number of items. In these situations, iterators allow you to process the datasets one item at a time without exhausting the memory resources of your system, which is one of the most attractive features of iterators.
Iterables
An object capable of returning its members one at a time. Examples of iterables include all sequence types (such as list
, str
, and tuple
) and some non-sequence types like dict
, file objects, and objects of any classes you define with an __iter__()
method or with a __getitem__()
method that implements sequence semantics.When using iterables, it is usually not necessary to call iter()
or deal with iterator objects yourself. The for
statement does that automatically for you, creating a temporary unnamed variable to hold the iterator for the duration of the loop
How to create an Iterator from Iterable?
We can create the iterator from iterable using iter()
function. Please look into below example:
cities = ["Ba sing se", "Omashu", "Republic city"]
# initialize the object
cities_iterator = iter(cities)
print(next(cities_iterator))
print(next(cities_iterator))
print(next(cities_iterator))
The output will be as below:
Ba sing se
Omashu
Republic city
You can also use for
loop with iterator :
cities = ["Ba sing se", "Omashu", "Republic city"]
# initialize the object
cities_iterator = iter(cities)
for item in cities_iterator:
print(item)
The output will be as below:
Ba sing se
Omashu
Republic city
When we are using for loop then we don't need to worry about the StopIteration
exception. It will be taken care of by for loop itself, which means it will exceed the index till the index where we won't receive the StopIteration
exception.
Note: Every iterator is also an iterable, but not every iterable is an iterator in Python.For example, a string is an iterable but not an iterator. We can check that from the below code:
name = "Toph"
print(next(name))
The output will give an error:
ERROR!
Traceback (most recent call last):
File "<string>", line 5, in <module>
TypeError: 'str' object is not an iterator
But if you convert "name" string in iterator using iter()
function as shown below:
name = "Toph"
iter_name = iter(name)
print(next(iter_name))
print(next(iter_name))
print(next(iter_name))
print(next(iter_name))
The output will be as below:
T
o
p
h
Now let's move on to our next topic "Generators"
Generators
Generator functions allow you to declare a function that behaves like an iterator, i.e. it can be used in a for loop. The main difference between an iterator and a generator is the "yield" keyword. In normal function, we use the "return" keyword but the generator function requires the "yield" keyword.
Note: "return" keyword will terminate the scope of the function but in the case of the "yield" keyword it will pause the execution of the code until it encounters the next "yield" keyword. Below is the diagram of how the "yield" keyword works:
Below is the example code of the Python generator:
def gen_city():
cities = ["Ba sing se", "Omashu", "Republic city"]
# initialize the object
for item in cities:
yield(item)
gen_city_obj=gen_city()
print(gen_city_obj)
for el in gen_city_obj:
print(el)
The output will be as below:
<generator object gen_city at 0x7fb9fd08b5e0>
Ba sing se
Omashu
Republic city
We can also generate the generators using generator expression, let's see how to do that.
Python Generator Expression:
In Python, a generator expression is a concise way to create a generator object. The syntax of Generator Expression is similar to List Comprehension except it uses parentheses ( ) instead of square brackets [ ]. Example of a generator expression is below:
squares = (i*i for i in range(5))
print(squares)
for n in squares:
print(n)
The output will be:
<generator object <genexpr> at 0x7efc730e75e0>
0
1
4
9
16
You can see that when we print squares we will see <generator object <genexpr> at 0x7efc730e75e0> which means the generator object is generated with a generator expression.
What we learned in this article:
What are iterators
When to use iterators in Python
What are Iterables
How to create an iterator from iterable
What are Generators
What is Python Generator Expression