Гайд по магическим методам в Python
Руководство по магическим методам, или dunder-методам Питона, которые предназначены для перегрузки Python-операторов или встроенных методов.
48К открытий60К показов
Магические методы в языке программирования Python нужны, чтобы реализовывать свойства объектов при их взаимодействии.
В статье раскроем их «магию» и остановимся на каждом из методов подробно.
Андрей Мальчук
Бэкенд разработчик группы частных облаков КРОК.
Создание и удаление объектов
Любое описание объекта в объектно-ориентированном программировании начинается с создания объекта и его удаления. Давайте подробнее остановимся на каждом из них:
__new__(cls[, ...])
— метод создания типа класса. Он принимает первым аргументом тип класса, в котором он вызывается, и, как правило, возвращает этот же тип. В основном используется, чтобы настраивать создание экземпляра класса тех объектов, которые наследуются от неизменяемых типов (например, int
, str
, или tuple
).
__init__(self[, ...])
— конструктор класса. Используется при определении объектов.
__init_subclass__(cls)
— позволяет переопределить создание подклассов объекта. Например, добавлять дополнительные атрибуты:
__del__(self)
— деструктор класса. Вызывается автоматически сборщиком мусора, практически никогда не используется, за исключением, когда пользователя необходимо предупредить о незакрытых дескрипторах.
Общие свойства объектов
Любой объект может содержать дополнительную информацию, полезную при отладке или приведении типов. Например:
__repr__(self)
— информационная строка об объекте. Выводится при вызове функции repr(...)
или в момент отладки. Для последнего этот метод и предназначен. Например:
__str__(self)
— вызывается при вызове функции str(...)
, возвращает строковый объект. Например:
__bytes__(self)
— аналогично __str__(self)
, только возвращается набор байт.
__format__(self, format_spec)
— вызывается при вызове функции format(...)
и используется для форматировании строки с использованием строковых литералов.
Методы сравнения объектов между собой
__lt__(self, other)
— определяет поведение оператора сравнения «меньше», <
.
__le__(self, other)
— определяет поведение оператора сравнения «меньше или равно», <=
.
__eq__(self, other)
— определяет поведение оператора «равенства», ==
.
__ne__(self, other)
— определяет поведение оператора «неравенства», !=
.
__gt__(self, other)
— определяет поведение оператора сравнения «больше», >
.
__ge__(self, other)
— определяет поведение оператора сравнения «больше или равно», >=
.
__hash__(self)
— вызывается функцией hash(...)
и используется для определения контрольной суммы объекта, чтобы доказать его уникальность. Например, чтобы добавить объект в set
, frozenset
, или использовать в качестве ключа в словаре dict
.
__bool__(self)
— вызывается функцией bool(...)
и возвращает True
или False
в соответствии с реализацией. Если данный метод не реализован в объекте, и объект является какой-либо последовательностью (списком, кортежем и т.д.), вместо него вызывается метод __len__
. Используется, в основном, в условиях if
, например:
Доступ к атрибутам объекта
Доступ ко всем свойствам объекта также контролируются отдельными методами:
__getattr__(self, name)
— вызывается методом getattr(...)
или при обращении к атрибуту объекта через x.y
, где x
— объект, а y
— атрибут.
__setattr__(self, name, value)
— вызывается методом setattr(...)
или при обращении к атрибуту объекта с последующим определением значения переданного атрибута. Например: x.y = 1
, где x
— объект, y
— атрибут, а 1
— значение атрибута.
__delattr__(self, name)
— вызывается методом delattr(...)
или при ручном удалении атрибута у объекта с помощью del x.y
, где x
— объект, а y
— атрибут.
__dir__(self)
— вызывается методом dir(...)
и выводит список доступных атрибутов объекта.
Создание последовательностей
Любой объект может реализовать методы встроенных последовательностей (словари, кортежи, списки, строки и так далее). Доступ к значениям последовательности переопределяется следующими методами:
__len__(self)
— вызывается методом len(...)
и возвращает количество элементов в последовательности.
__getitem__(self, key)
— вызывается при обращении к элементу в последовательности по его ключу (индексу). Метод должен выбрасывать исключение TypeError
, если используется некорректный тип ключа, KeyError
, если данному ключу не соответствует ни один элемент в последовательности. Например:
__setitem__(self, key, value)
— вызывается при присваивании какого-либо значения элементу в последовательности. Также может выбрасывать исключения TypeError
и KeyError
. Например:
__delitem__(self, key)
— вызывается при удалении значения в последовательности по его индексу (ключу) с помощью синтаксиса ключевого слова del
.
__missing__(self, key)
— вызывается в случаях, когда значения в последовательности не существует.
__iter__(self)
— вызывается методом iter(...)
и возвращает итератор последовательности, например, для использования объекта в цикле:
__reversed__(self)
— вызывается методом reversed(...)
и аналогично методу __iter__
возвращает тот же итератор, только в обратном порядке.
__contains__(self, item)
— вызывается при проверке принадлежности элемента к последовательности с помощью in
или not in
.
Числовые магические методы
Данные методы делятся на несколько групп: унарные операторы, обычные арифметические, отражённые арифметические операторы, составные присваивания и преобразования типов.
Унарные операторы
__neg__(self)
— определяет поведение для отрицания (-a
)
__pos__(self)
— определяет поведение для унарного плюса (+a
)
__abs__(self)
— определяет поведение для встроенной функции abs(...)
__invert__(self)
— определяет поведение для инвертирования оператором ~
Обычные арифметические операторы
__add__(self, other)
— сложение, оператор +
__sub__(self, other)
— вычитание, оператор -
__mul__(self, other)
— умножение, оператор *
__matmul__(self, other)
— умножение матриц, оператор @
__truediv__(self, other)
— деление, оператор /
__floordiv__(self, other)
— целочисленное деление, оператор //
__mod__(self, other)
— остаток от деления, оператор %
__divmod__(self, other)
— деление с остатком, определяет поведение для встроенной функции divmod(...)
__pow__(self, other[, modulo])
— возведение в степень, оператор **
__lshift__(self, other)
— двоичный сдвиг влево, оператор <<
__rshift__(self, other)
— двоичный сдвиг вправо, оператор >>
__and__(self, other)
— двоичное И, оператор &
__xor__(self, other)
— исключающее ИЛИ, оператор ^
__or__(self, other)
— двоичное ИЛИ, оператор |
Отражённые арифметические операторы
Если в обычной арифметике между объектами a
и b
, объектом, который мы изменяем, является a
, и объектом, с которым мы работаем, являетсяb
, то в отражённой арифметике наоборот — b
является изменяемым, a
— объектом, с которым мы работаем, и который передается в качестве аргумента. Например:
Список методов похож на тот, что используется в обычной арифметике, за исключением того, что добавляется префикс «r» ко всем методам:
__radd__(self, other)
— сложение, оператор +
__rsub__(self, other)
— вычитание, оператор -
__rmul__(self, other)
— умножение, оператор *
__rmatmul__(self, other)
— умножение матриц, оператор @
__rtruediv__(self, other)
— деление, оператор /
__rfloordiv__(self, other)
— целочисленное деление, оператор //
__rmod__(self, other)
— остаток от деления, оператор %
__rdivmod__(self, other)
— деление с остатком
__rpow__(self, other[, modulo])
— возведение в степень, оператор **
__rlshift__(self, other)
— двоичный сдвиг влево, оператор <<
__rrshift__(self, other)
— двоичный сдвиг вправо, оператор >>
__rand__(self, other)
— двоичное И, оператор &
__rxor__(self, other)
— исключающее ИЛИ, оператор ^
__ror__(self, other)
— двоичное ИЛИ, оператор |
Составное присваивание
Эти методы — комбинация «обычного» оператора и присваивания. Возвращают тот же тип объекта, который будет присвоен переменной слева. Например:
__iadd__(self, other)
— сложение с присваиванием, оператор +=
__isub__(self, other)
— вычитание с присваиванием, оператор -=
__imul__(self, other)
— умножение с присваиванием, оператор *=
__imatmul__(self, other)
— умножение матриц с присваиванием, оператор @=
__itruediv__(self, other)
— деление с присваиванием, оператор /=
__ifloordiv__(self, other)
— целочисленное деление с присваиванием, оператор //=
__imod__(self, other)
— остаток от деления с присваиванием, оператор %=
__ipow__(self, other[, modulo])
— возведение в степень с присваиванием, оператор **=
__ilshift__(self, other)
— двоичный сдвиг влево с присваиванием, оператор <<=
__irshift__(self, other)
— двоичный сдвиг вправо с присваиванием, оператор >>=
__iand__(self, other)
— двоичное И с присваиванием, оператор &=
__ixor__(self, other)
— исключающее ИЛИ с присваиванием, оператор ^=
__ior__(self, other)
— двоичное ИЛИ с присваиванием, оператор |=
Преобразования типов
Помимо всего прочего, в Python множество методов, которые позволяют переопределять поведение встроенных функций преобразования типов, таких как int(...)
, float(...)
и т.д. Например:
__complex__(self)
— преобразование типа в комплексное число
__int__(self)
— преобразование типа к int
__float__(self)
— преобразование типа к float
__index__(self)
— преобразование типа к int
, когда объект используется в срезах (выражения вида [start:stop:step]
)
__round__(self[, ndigits])
— округление числа с помощью функции round(...)
__trunc__(self)
— вызывается методом math.trunc(...)
__floor__(self)
— вызывается методом math.floor(...)
__ceil__(self)
— вызывается методом math.ceil(...)
Вызываемые объекты
__call__(self[, args...])
— позволяет любому экземпляру класса вести себя как обычная функция. Например:
__await__(self)
— возвращает итератор, превращая класс в корутину, результат выполнения которой можно получить с помощью await
. Подробнее об этом можно узнать в PEP 492.
Контекстные менеджеры
Любой объект может быть представлен как контекстный менеджер, который вызывается с помощью with
или async with
. Данная конструкция позволяет выполнить какие-либо действия по настройке объекта и при выходе из контекстного менеджера, произвести какие-либо действия по очистке, не смотря на то, было ли вызвано исключение в блоке контекстного менеджера.
__enter__(self)
— определяет начало блока контекстного менеджера, вызванного с помощью with
__exit__(self, exc_type, exc_value, traceback)
— определяет конец блока контекстного менеджера. Может использоваться для контролирования исключений, очистки, или любых действий, которые должны быть выполнены после блока внутри with
. Если блок выполнился успешно, то все три аргумента (exc_type
, exc_value
и traceback
) будут установлены в значение None
.
Например:
__aenter__(self)
— аналогично __enter__
, только функция возвращает корутину (результат которой можно получить с помощью await
)
__aexit__(self, exc_type, exc_value, traceback)
— аналогично __exit__
, только функция возвращает корутину (результат которой можно получить с помощью await
)
Например:
Неиспользуемые методы
Некоторые методы, после полного перехода с Python 2 на Python 3 стали устаревшими и больше не используются.
__unicode__
— полностью исчез в версии Python 3, вместо него используются отдельные методы __str__
и __bytes__
__div__
— так как в Python 3 теперь по умолчанию «правильное деление», данного метода не существует
__cmp__
— более не существует, вместо него используются __lt__
, __le__
, __eq__
, __ne__
, __gt__
и __ge__
__nonzero__
— переименован в __bool__
Упрощение работы с магическими методами
Большая часть из вышеописанных методов реализуется библиотеками, они позволяют использовать так называемые «обрёртки» над классами, в которых будут реализованы необходимые магические методы.
Пример таких библиотек:
Принципы и идеология этих (и других библиотек) схожи в одном – они позволяют реализовать всю необходимую логику работы с объектом, не дублируя код для каждого отдельного объекта.
Например:
Данный объект будет иметь в себе:
- Три атрибута
first_name
,last_name
иage
, которые также будут передаваться в конструктор класса; - Будет реализован метод
__repr__
для вывода информации для отладки; - Будут реализованы все магические методы сравнения (такие как
__lt__
,__eq__
и так далее), а также метод__hash__
.
Чаще всего вы будете пользоваться уже готовыми решениями, как пример выше, но для того чтобы понять, как работает язык Python изнутри, необходимо знать, как работают магические методы, за что они отвечают, что будет, если вызвать ту или иную функцию на объект, и что с этим объектом произойдёт.
48К открытий60К показов