Я Сергей
Балакирев и мы продолжаем знакомиться с магическими методами классов. Здесь
речь пойдет о следующем их наборе:
- __getitem__(self,
item) – получение значения по ключу item;
- __setitem__(self,
key, value) – запись
значения value по ключу key;
- __delitem__(self,
key) – удаление элемента по ключу key.
Давайте
разберемся для чего они нужны и как их можно использовать. Предположим, что мы
создаем класс для представления студентов:
class Student:
def __init__(self, name, marks):
self.name = name
self.marks = list(marks)
Его экземпляр
можно сформировать, следующим образом:
s1 = Student('Сергей', [5, 5, 3, 2, 5])
В объекте s1 имеется
локальное свойство marks со списком студентов. Мы можем к нему
обратиться и выбрать любую оценку:
Но что если мы
хотим делать то же самое, но используя только ссылку на объект s1:
Если сейчас
запустить программу, то увидим сообщение об ошибке, что наш класс (объект) не
поддерживает такой синтаксис. Как вы, наверное, уже догадались, поправить это
можно с помощью магического метода __getitem__. Запишем его
в нашем классе, следующим образом:
def __getitem__(self, item):
return self.marks[item]
Теперь ошибок
нет и на экране видим значение 3. Однако, если указать неверный индекс:
то получим
исключение IndexError, которое сгенерировал список marks. При
необходимости, мы можем сами контролировать эту ошибку, если в методе __getitem__ пропишем
проверку:
def __getitem__(self, item):
if 0 <= item < len(self.marks):
return self.marks[item]
else:
raise IndexError("Неверный индекс")
При запуске
программы видим наше сообщение «Неверный индекс». Также можно сделать проверку
на тип индекса:
для списков он
должен быть целым числом. Поэтому дополнительно можно записать такую проверку:
def __getitem__(self, item):
if not isinstance(item, int):
raise TypeError("Индекс должен быть целым числом")
if 0 <= item < len(self.marks):
return self.marks[item]
else:
raise IndexError("Неверный индекс")
То есть, здесь
возможны самые разные вариации обработки и проверки исходных данных, прежде чем
обратиться к списку marks и вернуть значение.
Теперь давайте
предположим, что хотели бы иметь возможность менять оценки студентов, используя
синтаксис:
Сейчас, после
запуска программы будет ошибка TypeError, что объект не поддерживает операцию
присвоения, так как в классе не реализован метод __setitem__. Давайте
добавим и его:
def __setitem__(self, key, value):
if not isinstance(key, int) or key < 0:
raise TypeError("Индекс должен быть целым неотрицательным числом")
self.marks[key] = value
Однако, если мы
сейчас укажем несуществующий индекс:
то операция
присвоения новой оценки приведет к ошибке. Если предполагается использовать
такую возможность, то реализовать ее можно, следующим образом:
def __setitem__(self, key, value):
if not isinstance(key, int) or key < 0:
raise TypeError("Индекс должен быть целым неотрицательным числом")
if key >= len(self.marks):
off = key + 1 - en(self.marks)
self.marks.extend([None]*off)
self.marks[key] = value
Если индекс
превышает размер списка, то мы расширяем список значениями None до нужной длины
(с помощью метода extend), а затем, в последний элемент
записываем переданное значение value. Теперь, при выполнении команд:
s1[10] = 4
print(s1.marks)
Увидим список:
[5, 5, 3, 2, 5, None, None, None, None, None, 4]
То есть, он был
расширен до 10 элементов и последним элементом записано 4. И так можно
прописывать любую нужную нам логику при записи новых значений в список marks.
Наконец,
последний третий магический метод __delitem__ вызывается
при удалении элемента из списка. Если сейчас записать команду:
то в консоли
увидим сообщение: «AttributeError: __delitem__». Здесь явно указывается, что
при удалении вызывается метод __delitem__. Добавим его в наш класс:
def __delitem__(self, key):
if not isinstance(key, int):
raise TypeError("Индекс должен быть целым числом")
del self.marks[key]
Теперь оценки
успешно удаляются, если указан верный индекс.
Вот общие
возможности данных магических методов. Теперь, если в программе вам потребуется
реализовать подобную логику поведения экземпляров классов, я думаю, вы легко со
всем справитесь.