Магические методы __str__, __repr__, __len__, __abs__
На этом занятии я расскажу о, так называемых, магических методах, которые определены в каждом классе и записываются через два подчеркивания вначале и в конце имен, например, так:
Как я говорил, их еще называют
dunder-методами (от англ. сокращения double underscope)
- __str__() – магический метод для отображения информации об объекте класса для пользователей (например, для функций print, str);
- __repr__() – магический метод для отображения информации об объекте класса в режиме отладки (для разработчиков).
class Cat: def __init__(self, name): self.name = name
При выводе cat, увидим служебную информацию:
def __repr__(self): return f": "
Обратите внимание, этот метод должен возвращать строку, поэтому здесь записан оператор return и формируемая строка. Что именно возвращать, мы решаем сами, в данном случае – это название класса и имя кошки. Переопределим измененный класс Cat. И, смотрите, теперь при создании экземпляра мы видим другую информацию при его выводе:
по-прежнему будем видеть служебную информацию от метода __repr__. Однако, если выполнить отображение экземпляра класса через print или str, то будет срабатывать уже второй метод __str__. Вот в этом отличие этих двух магических методов.
Магические методы __len__ и __abs__
- __len__() – позволяет применять функцию len() к экземплярам класса;
- __abs__() — позволяет применять функцию abs() к экземплярам класса.
class Point: def __init__(self, *args): self.__coords = args
А, далее, по программе нам бы хотелось определять размерность координат с помощью функции len(), следующим образом:
Если сейчас запустить программу, то увидим ошибку, так как функция len не применима к экземплярам классов по умолчанию. Как вы уже догадались, чтобы изменить это поведение, можно переопределить магический метод __len__() и в нашем случае это можно сделать так:
def __len__(self): return len(self.__coords)
Смотрите, мы здесь возвращаем размер списка __coords и если после этого запустить программу, то как раз это значение и будет выведено в консоль. То есть, магический метод __len__ указал, что нужно возвращать, в момент применения функции len() к экземпляру класса. Как видите, все просто и очевидно. Следующий магический метод __abs__ работает аналогичным образом, только активируется в момент вызова функции abs для экземпляра класса, например, так:
Опять же, если сейчас выполнить программу, то увидим ошибку, т.к. функция abs не может быть напрямую применена к экземпляру. Но, если переопределить магический метод:
def __abs__(self): return list( map(abs, self.__coords) )
Перегрузка операторов
Перегрузка операторов — один из способов реализации полиморфизма, когда мы можем задать свою реализацию какого-либо метода в своём классе.
Например, у нас есть два класса:
В данном примере класс B наследует класс A, но переопределяет метод go, поэтому он имеет мало общего с аналогичным методом класса A.
Однако в python имеются методы, которые, как правило, не вызываются напрямую, а вызываются встроенными функциями или операторами.
Например, метод __init__ перегружает конструктор класса. Конструктор — создание экземпляра класса.
__new__(cls[, . ]) — управляет созданием экземпляра. В качестве обязательного аргумента принимает класс (не путать с экземпляром). Должен возвращать экземпляр класса для его последующей его передачи методу __init__.
__init__(self[, . ]) - как уже было сказано выше, конструктор.
__del__(self) - вызывается при удалении объекта сборщиком мусора.
__repr__(self) - вызывается встроенной функцией repr; возвращает "сырые" данные, использующиеся для внутреннего представления в python.
__str__(self) - вызывается функциями str, print и format. Возвращает строковое представление объекта.
__bytes__(self) - вызывается функцией bytes при преобразовании к байтам.
__format__(self, format_spec) - используется функцией format (а также методом format у строк).
__le__(self, other) - x ≤ y вызывает x.__le__(y).
__eq__(self, other) - x == y вызывает x.__eq__(y).
__ne__(self, other) - x != y вызывает x.__ne__(y)
__gt__(self, other) - x > y вызывает x.__gt__(y).
__ge__(self, other) - x ≥ y вызывает x.__ge__(y).
__hash__(self) - получение хэш-суммы объекта, например, для добавления в словарь.
__bool__(self) - вызывается при проверке истинности. Если этот метод не определён, вызывается метод __len__ (объекты, имеющие ненулевую длину, считаются истинными).
__getattr__(self, name) - вызывается, когда атрибут экземпляра класса не найден в обычных местах (например, у экземпляра нет метода с таким названием).
__setattr__(self, name, value) - назначение атрибута.
__delattr__(self, name) - удаление атрибута (del obj.name).
__call__(self[, args. ]) - вызов экземпляра класса как функции.
__len__(self) - длина объекта.
__getitem__(self, key) - доступ по индексу (или ключу).
__setitem__(self, key, value) - назначение элемента по индексу.
__delitem__(self, key) - удаление элемента по индексу.
__iter__(self) - возвращает итератор для контейнера.
__reversed__(self) - итератор из элементов, следующих в обратном порядке.
__contains__(self, item) - проверка на принадлежность элемента контейнеру (item in self).
Перегрузка арифметических операторов
__add__(self, other) - сложение. x + y вызывает x.__add__(y).
__sub__(self, other) - вычитание (x - y).
__mul__(self, other) - умножение (x * y).
__truediv__(self, other) - деление (x / y).
__floordiv__(self, other) - целочисленное деление (x // y).
__mod__(self, other) - остаток от деления (x % y).
__divmod__(self, other) - частное и остаток (divmod(x, y)).
__pow__(self, other[, modulo]) - возведение в степень (x ** y, pow(x, y[, modulo])).
__lshift__(self, other) - битовый сдвиг влево (x
__rshift__(self, other) - битовый сдвиг вправо (x >> y).
__and__(self, other) - битовое И (x & y).
__xor__(self, other) - битовое ИСКЛЮЧАЮЩЕЕ ИЛИ (x ^ y).
__or__(self, other) - битовое ИЛИ (x | y).
__rtruediv__(self, other),
__rfloordiv__(self, other),
__rdivmod__(self, other),
__rlshift__(self, other),
__rrshift__(self, other),
__ror__(self, other) - делают то же самое, что и арифметические операторы, перечисленные выше, но для аргументов, находящихся справа, и только в случае, если для левого операнда не определён соответствующий метод.
Например, операция x + y будет сначала пытаться вызвать x.__add__(y), и только в том случае, если это не получилось, будет пытаться вызвать y.__radd__(x). Аналогично для остальных методов.
__itruediv__(self, other) - /=.
__ifloordiv__(self, other) - //=.
__ipow__(self, other[, modulo]) - **=.
__ilshift__(self, other) -
__irshift__(self, other) - >>=.
__neg__(self) - унарный -.
__pos__(self) - унарный +.
__abs__(self) - модуль (abs()).
__invert__(self) - инверсия (~).
__complex__(self) - приведение к complex.
__int__(self) - приведение к int.
__float__(self) - приведение к float.
__round__(self[, n]) - округление.
__enter__(self), __exit__(self, exc_type, exc_value, traceback) - реализация менеджеров контекста.
Рассмотрим некоторые из этих методов на примере двухмерного вектора, для которого переопределим некоторые методы:
В заключение хочу сказать, что перегрузка специальных методов - вещь хорошая, но не стоит ей слишком злоупотреблять. Перегружайте их только тогда, когда вы уверены в том, что это поможет пониманию программного кода.
Для вставки кода на Python в комментарий заключайте его в теги
- Книги о Python
- GUI (графический интерфейс пользователя)
- Курсы Python
- Модули
- Новости мира Python
- NumPy
- Обработка данных
- Основы программирования
- Примеры программ
- Типы данных в Python
- Видео
- Python для Web
- Работа для Python-программистов