Перейти до змісту

Реалізація за допомогою __getitem__

В Python коли ми отримуємо доступ до елементів контейнера конструкцією:

obj[key]

завжди викликається магічний метод:

__getitem__(obj, key)

Реалізуючи цей магічний метод ми і задаємо поведінку для екземплярів певних класів. Наприклад, для списків — це доступ до його елементів по індексу, в цьому випадку індексом може бути ціле число або зрізання, а для словників — це ключ елемента.

Пригадаємо: щоб об'єкт був ітерабельним, функція iter() має повернути ітератор для цього об'єкта. У свою чергу якщо функція iter() знаходить у потенційно ітерабельного об'єкта магічний метод __getitem__(), тоді Python спробує побудувати ітератор самостійно.

Методу __getitem__() будуть передаватись цілі невід'ємні числа, тобто як індекси для списків, але які більше або дорівнюють нулю. Ми ж у свою чергу повинні реалізувати повернення необхідного значення, яке начебто відповідає переданому індексу.

Якщо переданий "індекс" виходить за межі, атже нам треба ж якимось чином обмежити довжину послідовності, то ми маємо "підняти" виняток IndexError

Ступені двійки

Створимо ітерабельний об'єкт, щоб можна було б отримувати послідовність із ступенів числа 2:

>>> class Pow2:
...     def __init__(self, max_len):
...         self.max_len = max_len
...     def __getitem__(self, index):
...         if 0 <= index <= self.max_len:
...             return 2 ** index
...         else:
...             raise IndexError
...

Створимо екземпляр нашого ітерабельного об'єкта, і спочатку спробуємо звертатись до його елементів "традиційним" способом, тобто за допомогою індексування:

>>> p2 = Pow2(4)
>>> p2[0]
1
>>> p2[4]
16
>>> p2[5]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in __getitem__
IndexError
>>>

А тепер давайте спробуємо отримати ітератор для створеного ітерабельного об'єкта:

>>> it = iter(p2)
>>> it
<iterator object at 0x000001F7396AF240>
>>>

Python сам створив ітератор. Спробуємо за допомогою цього ітератора отримувати значення послідовності:

>>> next(it)
1
>>> next(it)
2
>>> next(it)
4
>>> next(it)
8
>>> next(it)
16
>>> next(it)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>

Зауважте: ітератор при досягненні кінця послідовності "піднімає" виняток StopIteration, власне як і має робити ітератор. Отже і цикл for буде працювати як і очікується:

>>> for n in p2: print(n)
...
1
2
4
8
16
>>>
Back to top