OOP technicalities

There is much to say about object oriented programming, here the focus is only made on few technical points of python objects useful later in the course.

Iterator

An iterator is an object with a __next__ method. It raises an StopIteration exception when there is no ‘next’ item.

Iterable

An iterable is an object with an __iter__ method which returns an iterator. It is a python object which can be seamlessly iterated over.

Callable

An object can be made callable if a __call__ method is defined. It useful when you need a function with a state.

Class attributes

Some attributes can be defined directly under the class declaration. These attributes are shares between all instances of the class.

Note

Careful! several very popular libraries use some dark magic to automatically define from class attributes the instance attributes for every instance. dataclass, attrs, django, pydantic etc.

src
 1import random
 2
 3
 4class RandomTraversalIterator:
 5    def __init__(self, items: list[int]) -> None:
 6        self.items = items
 7        self.indexes = list(range(len(items)))
 8        random.shuffle(self.indexes)
 9
10    def __next__(self) -> int:
11        if self.indexes:
12            return self.items[self.indexes.pop()]
13        raise StopIteration
14
15
16class RandomTraversalIterable:
17    def __init__(self, items: list[int]) -> None:
18        self.items = items
19
20    def __iter__(self):
21        return RandomTraversalIterator(self.items)
22
23
24class CheckAnswerMultipleGuess:
25    def __init__(self, question: str, correct_answer: int, nb_trial: int) -> None:
26        self.question = question
27        self.correct_answer = correct_answer
28        self.remaining = nb_trial
29        print(question)
30
31    def __call__(self, answer: int) -> str:
32        if self.remaining == 0:
33            return "no more guess"
34        if answer == self.correct_answer:
35            return "correct"
36        self.remaining -= 1
37        return f"incorrect, remains {self.remaining} guess(es)"
38
39
40def show_iterator():
41    numbers: list[int] = [10, 11, 12]
42    iterator = RandomTraversalIterator(numbers)
43    try:
44        print(next(iterator))
45        print(next(iterator))
46        print(next(iterator))
47        print(next(iterator))
48    except StopIteration:
49        print("no more item")
50
51
52def show_iterable():
53    numbers: list[int] = [20, 21, 22]
54    for item in RandomTraversalIterable(numbers):
55        print(item)
56
57
58def show_callable():
59    check = CheckAnswerMultipleGuess(
60        "guess number between 0-100", correct_answer=42, nb_trial=3
61    )
62    print(check(41))
63    print(check(40))
64    print(check(42))
65    print(check(43))
66    print(check(44))
67
68
69if __name__ == "__main__":
70    show_iterator()
71    show_iterable()
72    show_callable()