Перегрузка операторов в Python

Введение

Примеры

  • 4

    Методы магии / Дандер

    Магические (также называемые 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 
  • 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
    
     
  • 13

    Перегрузка оператора

    Ниже приведены операторы, которые могут быть перегружены в классах, а также необходимые определения методов и пример использования оператора в выражении.

    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
    & Побитовое __and__(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 a1
    (*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__ метод, если он существует. Некоторые из этих видов использования перечислены ниже.

    функция метод выражение
    Кастинг для 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__(self) math.floor(a1)
    Этаж __floor__(self) math.floor(a1)
    потолок __ceil__(self) math.ceil(a1)

    Есть также специальные методы __enter__ и __exit__ для менеджеров контекста, и многих других.

Синтаксис

Параметры

Примечания