Методы магии / Дандер
Магические (также называемые dunder как аббревиатура от двойного подчеркивания) методы в Python имеют ту же цель, что и перегрузка операторов в других языках. Они позволяют классу определять свое поведение, когда он используется в качестве операнда в выражениях унарных или бинарных операторов. Они также служат реализациями, вызываемыми некоторыми встроенными функциями.
Рассмотрим эту реализацию двумерных векторов.
import math
class Vector(object):
# instantiation
def __init__(self, x, y):
self.x = x
self.y = y
# unary negation (-v)
def __neg__(self):
return Vector(-self.x, -self.y)
# addition (v + u)
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
# subtraction (v - u)
def __sub__(self, other):
return self + (-other)
# equality (v == u)
def __eq__(self, other):
return self.x == other.x and self.y == other.y
# abs(v)
def __abs__(self):
return math.hypot(self.x, self.y)
# str(v)
def __str__(self):
return '<{0.x}, {0.y}>'.format(self)
# repr(v)
def __repr__(self):
return 'Vector({0.x}, {0.y})'.format(self)
Теперь можно , естественно , использовать экземпляры Vector
класса в различных выражениях.
v = Vector(1, 4)
u = Vector(2, 0)
u + v # Vector(3, 4)
print(u + v) # "<3, 4>" (implicit string conversion)
u - v # Vector(1, -4)
u == v # False
u + v == v + u # True
abs(u + v) # 5.0
Обработка невыполненного поведения
Если ваш класс не реализует конкретный перегруженный оператор для типов аргументов , предоставленных, он должен return NotImplemented
(обратите внимание , что это специальная константа , не то же самое , как NotImplementedError
). Это позволит Python попробовать другие методы, чтобы заставить работу работать:
Когда NotImplemented
возвращается, интерпретатор будет пытаться отраженную операцию на другой тип или какой - либо другой запасной вариант, в зависимости от оператора. Если все попытки операции возвращают NotImplemented
, интерпретатор выдаст соответствующее исключение.
Например, если x + y
, если x.__add__(y)
возвращает невыполненным, y.__radd__(x)
попытка вместо этого.
class NotAddable(object):
def __init__(self, value):
self.value = value
def __add__(self, other):
return NotImplemented
class Addable(NotAddable):
def __add__(self, other):
return Addable(self.value + other.value)
__radd__ = __add__
Как это отражается метод , который мы должны реализовать __add__
и __radd__
получить ожидаемое поведение во всех случаях; к счастью, так как они оба делают одно и то же в этом простом примере, мы можем воспользоваться ярлыком.
В использовании:
>>> x = NotAddable(1)
>>> y = Addable(2)
>>> x + x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'NotAddable' and 'NotAddable'
>>> y + y
<so.Addable object at 0x1095974d0>
>>> z = x + y
>>> z
<so.Addable object at 0x109597510>
>>> z.value
3
Перегрузка оператора
Ниже приведены операторы, которые могут быть перегружены в классах, а также необходимые определения методов и пример использования оператора в выражении.
NB Использование other
в качестве имени переменной не является обязательным, но считается нормой.
Оператора | Метод | Выражение |
---|---|---|
Сложение + |
__add__(self, other) |
a1 + a2 |
Вычитание - |
__sub__(self, other) |
a1 - a2 |
Умножение * |
__mul__(self, other) |
a1 * a2 |
Умножение матриц | __matmul__(self,other) |
a1 @ a2 (Python 3.5) |
Деление / |
__div__(self, other) |
a1 / a2 (Python 2) |
Деление / |
__truediv(self, other) |
a1 / a2 (Python 3) |
Целочисленное деление // | __floordiv__(self, other) |
a1 // a2 |
Модуло / Остаток | __mod__(self, other) |
a1 % a2 |
Степень | __pow__(self, other[, modulo]) |
a1 ** a2 |
Побитовый сдвиг влево << |
__lshift__(self, other) |
a1 << a2 |
Побитовый сдвиг вправо >> |
__rshift__(self, other) |
a1 >> a2 |
Побитовое сложение & |
__add__(self, other) |
a1 & a2 |
Побитовое исключающее ИЛИ ^ |
__xor__(self, other) |
a1 ^ a2 |
Побитовое | ИЛИ | __or__(self, other) |
a1 |a2 |
Отрицание - |
__neg__(self) |
-a1 |
Положительное + |
__pos__(self) |
+a1 |
Побитовое НЕ ~ |
__invert__(self) |
~a1 |
Меньше чем < |
__lt__(self, other) |
a1 < a2 |
Меньше или равно <= |
__le__(self, other) |
a1 <= a2 |
Равно == |
__eq__(self, other) |
a1 == a2 |
Не равно >= |
__ne__(self, other) |
a1 != a2 |
Больше чем > |
__gt__(self, other) |
a1 > a2 |
Больше или равно => |
__ge__(self, other) |
a1 >= a2 |
Индекс [index] |
__getitem__(self, index) |
a1[index] |
Оператор в in |
__contains__(self, other) |
a2 in a2 |
Вызов функции (*args, ...) |
__call__(self, *args, **kwargs) |
a1(*args, **kwargs) |
Необязательный параметр по modulo
для __pow__
используется только в pow
встроенной функции.
Каждый из методов , соответствующих бинарный оператор имеет соответствующий «правильный» метод , который начать с __r
, например __radd__
:
class A:
def __init__(self, a):
self.a = a
def __add__(self, other):
return self.a + other
def __radd__(self, other):
print("radd")
return other + self.a
A(1) + 2 # Out: 3
2 + A(1) # prints radd. Out: 3
а также соответствующая версия Inplace, начиная с __i
:
class B:
def __init__(self, b):
self.b = b
def __iadd__(self, other):
self.b += other
print("iadd")
return self
b = B(2)
b.b # Out: 2
b += 1 # prints iadd
b.b # Out: 3
Поскольку в этих методах нет ничего особенного, многие другие части языка, части стандартной библиотеки и даже сторонние модули самостоятельно добавляют магические методы, такие как методы для приведения объекта к типу или проверки свойств объекта. Например, встроенная str()
вызов функции объекта __str__
метод, если он существует. Некоторые из этих видов использования перечислены ниже.
Id | Метод | Выражение |
---|---|---|
Кастинг для int |
__int__(self) |
int(a1) |
Абсолютная функция | __abs__(self) |
abs(a1) |
Кастинг для str |
__str__(self) |
str(a1) |
Кастинг для unicode |
__unicode__(self) |
unicode(a1) python 2 |
Строковое представление | __repr__(self) |
repr(a1) |
Приведение к bool | __nonzero__(self) |
bool(a1) |
Форматирование строки | __format__(self, formatstr) |
"Hi {:abc}".format(a1) |
Хэшировние | __hash__(self) |
hash(a1) |
Длина | __len__(self) |
len(a1) |
Округление | __round__(self) |
round(a1) |
Обратное округление | __reversed__(self) |
reversed(a1) |
Floor | __floor__(self) |
math.floor(a1) |
Ceiling | __ceil__(self) |
math.ceil(a1) |
Есть также специальные методы __enter__
и __exit__
для менеджеров контекста, и многих других.