Published on

Python Iterators and Iterables

Authors

In this .......

Table of Contents

  1. Iterators And Iterables: the Basics
  2. With Iterator
  3. Using Iterators To Introduce Abstraction
  4. References

Iterators And Iterables: the Basics

Iterators and iterables are fundamental components of Python programming, and you’ll have to deal with them in almost all your programs. Learning how they work and how to create them is key for you as a Python developer.

In Python, an iterator is an object that allows you to iterate over collections of data, such as lists, tuples, dictionaries, and sets.

Python iterators implement the iterator design pattern, which allows you to traverse a container and access its elements. The iterator pattern decouples the iteration algorithms from container data structures.

Iterators take responsibility for two main actions:

  1. Returning the data from a stream or container one item at a time
  2. Keeping track of the current and visited items

Below we can hve a custom iterator

from dataclasses import dataclass
from typing import Self

@dataclass
class NumberIterator:
    max: int
    num: int = 0

    def __iter__(self) -> Self:
        return self

    def __next__(self) -> int:
        if self.num >= self.max:
            raise StopIteration
        self.num += 1
        return self.num


@dataclass
class InfiniteNumberIterator:
    num: int = 0

    def __iter__(self) -> Self:
        return self

    def __next__(self) -> int:
        self.num += 1
        return self.num


def main() -> None:
    for num in NumberIterator(3):
        print(num)


if __name__ == "__main__":
    main()

Results from the code above:

1
2
3

Building an iterator from scratch is easy in Python. We just have to implement the __iter__() that defines its iteration behavior and the __next__() methods,

  1. __iter__() returns the iterator object itself. If required, some initialization can be performed.
  2. __next__() must return the next item in the sequence. On reaching the end, and in subsequent calls, it must raise StopIterationexception.

With Iterator

Th eYou can read a file line by line, the sentinel value is a value that indicates the end of the stream in this case, the sentinel value is an empty string

# countries.txt on the root folder of the project
with open("countries.txt") as f:
   for line in iter(f.readline, ""):
      print(line, end="")

Results from the code above:

Germany
France
Italy
Spain
Netherlands
Greece

Using Iterators To Introduce Abstraction

Using the Iterable from typing we can provide annotations to the parameter of the function print_totals and the type of the variable line_items in the main function.

Iterable[LineItem] does not care if it is a list or a tuple, as long as it is iterable.

from dataclasses import dataclass
from typing import Iterable


@dataclass(frozen=True)
class LineItem:
    price: int
    quantity: int

    def total_price(self) -> int:
        return self.price * self.quantity


def print_totals(items: Iterable[LineItem]) -> None:
    for item in items:
        print(item.total_price())


def main() -> None:
    line_items = {
        LineItem(1, 2),
        LineItem(3, 4),
        LineItem(5, 6),
    }
    print_totals(line_items)


if __name__ == "__main__":
    main()

Results from the code above:

2
12
30

References: