Перегрузка арифметических операторов
__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:
Собственно, это и есть декораторы. Вот так можно было записать предыдущий пример, используя синтаксис декораторов:
@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()#выведет:## #помидоры## --ветчина--# ~салат~# <\______/>Также нужно помнить о том, что важен порядок декорирования. Сравните с предыдущим примером: