Перегрузка арифметических операторов

__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 << y).

__rshift__(self, other) - битовый сдвиг вправо (x >> y).

__and__(self, other) - битовое И (x & y).

__xor__(self, other) - битовое ИСКЛЮЧАЮЩЕЕ ИЛИ (x ^ y).

__or__(self, other) - битовое ИЛИ (x | y).

Пойдём дальше.

__radd__(self, other),

__rsub__(self, other),

__rmul__(self, other),

__rtruediv__(self, other),

__rfloordiv__(self, other),

__rmod__(self, other),

__rdivmod__(self, other),

__rpow__(self, other),

__rlshift__(self, other),

__rrshift__(self, other),

__rand__(self, other),

__rxor__(self, other),

__ror__(self, other) - делают то же самое, что и арифметические операторы, перечисленные выше, но для аргументов, находящихся справа, и только в случае, если для левого операнда не определён соответствующий метод.

Например, операция x + y будет сначала пытаться вызвать x.__add__(y), и только в том случае, если это не получилось, будет пытаться вызвать y.__radd__(x). Аналогично для остальных методов.

Идём дальше.

__iadd__(self, other) - +=.

__isub__(self, other) - -=.

__imul__(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) - |=.

__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) - реализация менеджеров контекста.

Рассмотрим некоторые из этих методов на примере двухмерного вектора, для которого переопределим некоторые методы:

import math class Vector2D: def __init__(self, x, y): self.x = x self.y = y def __repr__(self): return 'Vector2D({}, {})'.format(self.x, self.y) def __str__(self): return '({}, {})'.format(self.x, self.y) def __add__(self, other): return Vector2D(self.x + other.x, self.y + other.y) def __iadd__(self, other): self.x += other.x self.y += other.y return self def __sub__(self, other): return Vector2D(self.x - other.x, self.y - other.y) def __isub__(self, other): self.x -= other.x self.y -= other.y return self def __abs__(self): return math.hypot(self.x, self.y) def __bool__(self): return self.x != 0 and self.y != 0 def __neg__(self): return Vector2D(-self.x, -self.y) >>> x = Vector2D(3, 4)>>> xVector2D(3, 4)>>> print(x)(3, 4)>>> abs(x)5.0>>> y = Vector2D(5, 6)>>> yVector2D(5, 6)>>> x + yVector2D(8, 10)>>> x - yVector2D(-2, -2)>>> -xVector2D(-3, -4)>>> x += y>>> xVector2D(8, 10)>>> bool(x)True>>> z = Vector2D(0, 0)>>> bool(z)False>>> -zVector2D(0, 0)

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

Декораторы

Итак, что же это такое? Для того, чтобы понять, как работают декораторы, в первую очередь следует вспомнить, что функции в python являются объектами, соответственно, их можно возвращать из другой функции или передавать в качестве аргумента. Также следует помнить, что функция в python может быть определена и внутри другой функции.

Вспомнив это, можно смело переходить к декораторам. Декораторы — это, по сути, "обёртки", которые дают нам возможность изменить поведение функции, не изменяя её код.

Создадим свой декоратор "вручную":

# Декоратор - это функция, ожидающая ДРУГУЮ функцию в качестве параметраdef my_shiny_new_decorator(function_to_decorate): # Внутри себя декоратор определяет функцию-"обёртку". Она будет обёрнута вокруг декорируемой, получая возможность исполнять произвольный код до и после неё. def the_wrapper_around_the_original_function(): print("Я - код, который отработает до вызова функции") function_to_decorate() # Сама функция print("А я - код, срабатывающий после") # Теперь, вернём функцию-обёртку, которая содержит в себе декорируемую функцию, и код, который необходимо выполнить до и после. return the_wrapper_around_the_original_function # Представим теперь, что у нас есть функция, которую мы не планируем больше трогать.def stand_alone_function(): print("Я простая одинокая функция, ты ведь не посмеешь меня изменять?")stand_alone_function()# выведет: Я простая одинокая функция, ты ведь не посмеешь меня изменять? # Однако, чтобы изменить её поведение, мы можем декорировать её, то есть просто передать декоратору, который обернет исходную функцию в любой код, который нам потребуется, и вернёт новую, готовую к использованию функцию:stand_alone_function_decorated = my_shiny_new_decorator(stand_alone_function)stand_alone_function_decorated()#выведет:# Я - код, который отработает до вызова функции# Я простая одинокая функция, ты ведь не посмеешь меня изменять?# А я - код, срабатывающий после

Наверное, теперь мы бы хотели, чтобы каждый раз, во время вызова stand_alone_function, вместо неё вызывалась stand_alone_function_decorated. Для этого просто перезапишем stand_alone_function:

stand_alone_function = my_shiny_new_decorator(stand_alone_function)stand_alone_function()#выведет:# Я - код, который отработает до вызова функции# Я простая одинокая функция, ты ведь не посмеешь меня изменять?..# А я - код, срабатывающий после

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

@my_shiny_new_decoratordef another_stand_alone_function(): print("Оставь меня в покое") another_stand_alone_function()#выведет:# Я - код, который отработает до вызова функции# Оставь меня в покое# А я - код, срабатывающий после

То есть, декораторы в python — это просто синтаксический сахар для конструкций вида:

another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function)

При этом, естественно, можно использовать несколько декораторов для одной функции, на пример так:

def bread(func): def wrapper(): print("") func() print("<\______/>") return wrapper def ingredients(func): def wrapper(): print("#помидоры#") func() print("~салат~") return wrapper def sandwich(food="--ветчина--"): print(food) sandwich()#выведет: --ветчина--sandwich = bread(ingredients(sandwich))sandwich()#выведет:## #помидоры## --ветчина--# ~салат~# <\______/>

И используя синтаксис декораторов:

@bread@ingredientsdef sandwich(food="--ветчина--"): print(food) sandwich()#выведет:## #помидоры## --ветчина--# ~салат~# <\______/>

Также нужно помнить о том, что важен порядок декорирования. Сравните с предыдущим примером:

Наши рекомендации