Python - Iterators



Iterator in Python is an object representing a stream of data. It follows iterator protocol which requires it to support __iter__() and __next__() methods. Python's built-in method iter() implements __iter__() method. It receives an iterable and returns iterator object. The built-in next() function internally calls to the iterator's __next__() method returns successive items in the stream. When no more data are available a StopIteration exception is raised.

Python uses iterators are implicitly while working with collection data types such as list, tuple or string. That's why these data types are called iterables. We normally use for loop to iterate through an iterable as follows −

for element in sequence:
 print (element)

Python's built-in method iter() implements __iter__() method. It receives an iterable and returns iterator object.

Example

Following code obtains iterator object from sequence types list, string and tuple. The iter() function also returns keyiterator from dictionary. However, int id not iterable, hence it produces TypeError.

print (iter("aa"))
print (iter([1,2,3]))
print (iter((1,2,3)))
print (iter({}))
print (iter(100))

It will produce the following output

<str_ascii_iterator object at 0x000001BB03FFAB60>
<list_iterator object at 0x000001BB03FFAB60>
<tuple_iterator object at 0x000001BB03FFAB60>
<dict_keyiterator object at 0x000001BB04181670>
Traceback (most recent call last):
   File "C:\Users\user\example.py", line 5, in <module>
      print (iter(100))
            ^^^^^^^^^
TypeError: 'int' object is not iterable

Iterator object has __next__() method. Every time it is called, it returns next element in iterator stream. When the stream gets exhausted, StopIteration error is raised. Call to next() function is equivalent to calling __next__() method of iterator object.

Example

it = iter([1,2,3])
print (next(it))
print (it.__next__())
print (it.__next__())
print (next(it))

It will produce the following output

1
2
3
Traceback (most recent call last):
   File "C:\Users\user\example.py", line 5, in <module>
      print (next(it))
            ^^^^^^^^
StopIteration

Example

You can use exception handling mechanism to catch StopIteration.

it = iter([1,2,3, 4, 5])
print (next(it))
while True:
   try:
      no = next(it)
      print (no)
   except StopIteration:
      break

It will produce the following output

1
2
3
4
5

To define a custom iterator class in Python, the class must define __iter__() and __next__() methods.

In the following example, the Oddnumbers is a class implementing __iter__() and __next__() methods. On every call to __next__(), the number increments by 2, thereby streaming odd numbers in the range 1 to 10.

Example

class Oddnumbers:

   def __init__(self, end_range):
      self.start = -1
      self.end = end_range

   def __iter__(self):
      return self

   def __next__(self):
      if self.start < self.end-1:
         self.start += 2
         return self.start
      else:
         raise StopIteration

countiter = Oddnumbers(10)
while True:
   try:
      no = next(countiter)
      print (no)
   except StopIteration:
      break

It will produce the following output

1
3
5
7
9

Asynchronous Iterator

Two built-in functions aiter() and anext() have been added in Python 3.10 version onwards. The aiter() function returns an asynchronous iterator object. It is an asynchronous counter part of the classical iterator. Any asynchronous iterator must support __aiter__() and __anext__() methods. These methods are internally called by the two built-in functions.

Like the classical iterator, the asynchronous iterator gives a stream of objects. When the stream is exhausted, the StopAsyncIteration exception is raised.

In the example give below, an asynchronous iterator class Oddnumbers is declared. It implements __aiter__() and __anext__() method. On each iteration, a next odd number is returned, and the program waits for one second, so that it can perform any other process asynchronously.

Unlike regular functions, asynchronous functionsare called coroutines and are executed with asyncio.run() method. The main() coroutine contains a while loop that successively obtains odd numbers and raises StopAsyncIteration if the number exceeds 9.

Example

import asyncio

class Oddnumbers():
   def __init__(self):
      self.start = -1

   def __aiter__(self):
      return self
      
   async def __anext__(self):
      if self.start >= 9:
         raise StopAsyncIteration
      self.start += 2
      await asyncio.sleep(1)
      return self.start
      
async def main():
   it = Oddnumbers()
   while True:
      try:
         awaitable = anext(it)
         result = await awaitable
         print(result)
      except StopAsyncIteration:
         break
         
asyncio.run(main())

It will produce the following output

1
3
5
7
9
Advertisements