Сообщить об ошибке.

Специальные (магические) методы класса Python

Класс может реализовывать определенные операции, которые вызываются специальным синтаксисом (например, арифметические операции или индексирование и срезы), определяя методы со специальными именами. Это подход Python к перегрузке операторов, позволяющий классам определять собственное поведение по отношению к операторам языка. Например, если класс определяет метод с именем __getitem__(), а x является экземпляром этого класса, то x[i] примерно эквивалентен type(x).__getitem__(x, i). Если соответствующий метод не определен, то попытки выполнить операцию вызывают исключение (обычно AttributeError или TypeError).

Установка для специального метода значения None означает, что соответствующая операция недоступна. Например, если класс устанавливает для __iter__() значение None, то класс не является итерируемым, поэтому вызов функции iter() в его экземплярах вызовет ошибку TypeError, без возврата к __getitem__().

При реализации класса, эмулирующего любой встроенный тип, важно, чтобы эмуляция была реализована только в той степени, в которой это имеет смысл для моделируемого объекта. Например, некоторые последовательности могут хорошо работать с извлечением отдельных элементов, но извлечение фрагмента среза может не иметь смысла. Одним из примеров этого является интерфейс NodeList в объектной модели документа W3C.

Как и где использовать магические методы?

Применение магических методов на примере __getitem__().

Представьте себе класс, который моделирует здание. В рамках данных для здания он включает в себя ряд атрибутов, включая описания компаний, которые занимают каждый этаж :

Без использования специального метода __getitem__() получился бы такой класс:

class Building(object):
     def __init__(self, floors):
         self._floors = [None]*floors
     def occupy(self, floor_number, data):
          self._floors[floor_number] = data
     def get_floor_data(self, floor_number):
          return self._floors[floor_number]


# Строим здание с 4 этажами
building = Building(4)
building.occupy(0, 'Reception')
building.occupy(1, 'ABC Corp')
building.occupy(2, 'DEF Inc')
print(building.get_floor_data(2))

Можно использовать магический метод __getitem__ и его аналог __setitem__, чтобы сделать использование класса Building() более приятным.

class Building(object):
     def __init__(self, floors):
         self._floors = [None]*floors
     def __setitem__(self, floor_number, data):
          self._floors[floor_number] = data
     def __getitem__(self, floor_number):
          return self._floors[floor_number]

# Строим здание с 4 этажами
building = Building(4)
building[0] = 'Reception'
building[1] = 'ABC Corp'
building[2] = 'DEF Inc'
print(building[2])

Используя в примере специальные методы __getitem__ и __setitem__ таким образом, мы решили рассматривать здание как контейнер этажей. Также можно реализовать итератор для класса Building() и, возможно, даже взятие среза - то есть получать данные более чем с одного этажа за раз - это зависит от того, какие действия требуются от реализации конкретного класса.