5.1. Iterators

5.1.1. Protocol

  • __iter__(self) -> self

  • __next__(self) -> raise StopIteration

5.1.2. Mechanism

5.1.2.1. For loop

Listing 5.63. For loop
DATA = [1, 2, 3]

for current in DATA:
    print(current)

5.1.2.2. Intuitive implementation of the for loop

Listing 5.64. Intuitive implementation of the for loop
DATA = [1, 2, 3]

iterator = DATA.__iter__()

try:
    current = iterator.__next__()
    print(current)

    current = iterator.__next__()
    print(current)

    current = iterator.__next__()
    print(current)

    current = iterator.__next__()
    print(current)
except StopIteration:
    pass

5.1.3. Iterating over objects

5.1.3.1. Iterating sequences

for number in [1, 2, 3]:
    print(number)

# 1
# 2
# 3
for key, value in [('a',1), ('b',2), ('c',3)]:
    print(f'{key} -> {value}')

# a -> 1
# b -> 2
# c -> 3

5.1.3.2. Iterating over dict

DATA = {'a': 1, 'b': 2, 'c': 3}

for element in DATA:
    print(element)

# a
# b
# c
for key, value in DATA.items():
    print(f'{key} -> {value}')

# a -> 1
# b -> 2
# c -> 3

5.1.3.3. Iterating over str

for character in 'hello':
    print(character)

# h
# e
# l
# l
# o

5.1.4. Own Implementation

class Parking:
    def __init__(self):
        self._parked_cars = list()

    def park(self, car):
        self._parked_cars.append(car)

    def __iter__(self):
        self._current_element = 0
        return self

    def __next__(self):
        if self._current_element >= len(self._parked_cars):
            raise StopIteration

        result = self._parked_cars[self._current_element]
        self._current_element += 1
        return result


parking = Parking()
parking.park('Mercedes')
parking.park('Maluch')
parking.park('Toyota')


for car in parking:
    print(car)

# Mercedes
# Maluch
# Toyota

5.1.5. itertools

5.1.5.1. chain()

keys = ['a', 'b', 'c']
values = [1, 2, 3]

for x in chain(keys, values):
    print(x)

# a
# b
# c
# 1
# 2
# 3
from itertools import chain


class Character:
    def __init__(self, *values):
        self.values = values
        self._iter_index = 0

    def __iter__(self):
        self._iter_index = 0
        return self

    def __next__(self):
        if self._iter_index >= len(self.values):
            raise StopIteration

        element = self.values[self._iter_index]
        self._iter_index += 1
        return element


class Number:
    def __init__(self, *values):
        self.values = values
        self._iter_index = 0

    def __iter__(self):
        self._iter_index = 0
        return self

    def __next__(self):
        if self._iter_index >= len(self.values):
            raise StopIteration

        element = self.values[self._iter_index]
        self._iter_index += 1
        return element


chars = Character('a', 'b', 'c')
nums = Number(1, 2, 3)

print(chain(chars, nums))
# <itertools.chain object at 0x1008ca0f0>

print(list(chain(chars, nums)))
# [1, 2, 3, 'a', 'b', 'c']

for x in chain(chars, nums):
    print(x)

# a
# b
# c
# 1
# 2
# 3

5.1.5.2. cycle()

from itertools import cycle

DATA = ['even', 'odd']

for x in cycle(DATA):
    print(x)

# even
# odd
# even
# odd
# even
# ...
from itertools import cycle

DATA = ['even', 'odd']

for i, status in enumerate(cycle(DATA)):
    print(i, status)

    if i == 3:
        break

# 0, even
# 1, odd
# 2, even
# 3, odd

5.1.6. Assignments

5.1.6.1. Protocol Iterator Usage

English
  1. Write own implementation of a range() function

  2. Use iterator protocol

  3. Arguments: start, stop, step

  4. How to implement passing only stop argument?

Polish
  1. Zaimplementuj własne rozwiązanie range()

  2. Use iterator protocol

  3. Argumenty: początek, koniec, krok

  4. Jak zaimplementować możliwość podawania tylko końca?

5.1.6.2. Protocol Iterator Implementation

English
  1. Use data from "Input" section (see below)

  2. Modify classes to implement iterator

  3. Iterate over object using for loop

  4. Print data

  5. Compare result with "Output" section (see below)

Polish
  1. Użyj data z sekcji "Input" (patrz poniżej)

  2. Zmodyfikuj klasy aby zaimplementować protokół iterator

  3. Iteruj po obiekcie używając pętli for

  4. Wypisz dane

  5. Porównaj wyniki z sekcją "Output" (patrz poniżej)

Input
from dataclasses import dataclass


@dataclass
class Astronaut:
    first_name: str
    last_name: str
    missions: tuple = ()

@dataclass
class Mission:
    year: int
    name: str


twardowski = Astronaut('Jan', 'Twardowski', missions=(
    Mission(1969, 'Apollo 11'),
    Mission(2024, 'Artemis 3'),
    Mission(2035, 'Ares 3'),
))
Output
Mission(year=1969, name='Apollo 11')
Mission(year=2024, name='Artemis 3')
Mission(year=2035, name='Ares 3')