1. Iterators

1.1. Protocol

  • __iter__()

  • __next__() -> raise StopIteration

1.2. Mechanism

1.2.1. For loop

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

for current in DATA:
    print(current)

1.2.2. Intuitive implementation of the for loop

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

try:

    current = DATA.__next__()
    print(current)

    current = DATA.__next__()
    print(current)

    current = DATA.__next__()
    print(current)

    current = DATA.__next__()
    print(current)

except StopIteration:
    pass

1.3. Iterating over objects

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

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

1.3.3. Iterating over str

for character in 'hello':
    print(character)

# h
# e
# l
# l
# o

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

1.5. itertools

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 Keys:
    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 Values:
    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


keys = Keys('a', 'b', 'c')
values = Values(1, 2, 3)

print(chain(keys, values))
# <itertools.chain object at 0x1008ca0f0>

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

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

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

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)

# 0, even
# 1, odd
# 2, even
# ...

1.6. Assignments

1.6.1. Range

  • Complexity level: easy

  • Lines of code to write: 5 lines

  • Estimated time of completion: 10 min

  • Filename: solution/iterator_range.py

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?

1.6.2. Own implementation

English
  1. For input data (see below)

  2. Modify classes to implement iterator

Polish
  1. Dla danych wejściowych (patrz poniżej)

  2. Zmodyfikuj klasy aby zaimplementować protokół iterator

Input
Listing 355. Struktury danych książki adresowej
from dataclasses import dataclass


@dataclass
class Contact:
    first_name: str
    last_name: str
    addresses: tuple = ()

@dataclass
class Address:
    location: str
    city: str


INPUT = Contact(first_name='Jan', last_name='Twardowski', addresses=(
    Address(location='Johnson Space Center', city='Houston, TX'),
    Address(location='Kennedy Space Center', city='Merritt Island, FL'),
    Address(location='Jet Propulsion Laboratory', city='Pasadena, CA'),
))

for address in INPUT:
    print(address)

# Address(location='Johnson Space Center', city='Houston, TX')
# Address(location='Kennedy Space Center', city='Merritt Island, FL')
# Address(location='Jet Propulsion Laboratory', city='Pasadena, CA')