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

Последнее обновление: 04.02.2024

Python позволяет определять для своих классов встроенные операторы, такие как операции сложения, вычитания и т.д. Для этого в модуле operator определен ряд функций:

Операция

Синтаксис

Функция

Сложение

a + b

__add__(a, b)

Объединение

seq1 + seq2

__concat__(seq1, seq2)

Проверка наличия

obj in seq

__contains__(seq, obj)

Деление

a / b

__truediv__(a, b)

Деление

a // b

__floordiv__(a, b)

Поразрядное И

a & b

__and__(a, b)

Поразрядное XOR

a ^ b

__xor__(a, b)

Поразрядная инверсия

~ a

__invert__(a)

Поразрядное ИЛИ

a | b

__or__(a, b)

Степень

a ** b

__pow__(a, b)

Присвоение по индексу

obj[k] = v

__setitem__(obj, k, v)

Удаление по индексу

del obj[k]

__delitem__(obj, k)

Обращение по индексу

obj[k]

__getitem__(obj, k)

Сдвиг влево

a << b

__lshift__(a, b)

Остаток от деления

a % b

__mod__(a, b)

Умножение

a * b

__mul__(a, b)

Умножение матриц

a @ b

__matmul__(a, b)

Арифметическое отрицание

-a

__neg__(a)

Логическое отрицание

not a

__not__(a)

Положительное значение

+a

__pos__(a)

Сдвиг вправо

a >> b

__rshift__(a, b)

Установка диапазона

seq[i:j] = values

__setitem__(seq, slice(i, j), values)

Удаление диапазона

del seq[i:j]

__delitem__(seq, slice(i, j))

Получение диапазона

seq[i:j]

__getitem__(seq, slice(i, j))

Вычитание

a - b

__sub__(a, b)

Проверка на Truе/False

obj

__bool__(obj)

Меньше чем

a < b

__lt__(a, b)

Меньше чем или равно

a <= b

__le__(a, b)

Равенство

a==b

__eq__(a, b)

Неравенство

a != b

__ne__(a, b)

Больше чем или равно

a >= b

__ge__(a, b)

Больше чем

a > b

__gt__(a, b)

Сложение с присваиванием

a += b

__iadd__(a, b)

Объединение с присваиванием

a += b

__iconcat__(a, b)

Поразрядное умножение с присваиванием

a &= b

__iand__(a, b)

Деление с присваиванием

a //= b

__ifloordiv__(a, b)

Сдвиг влево с присваиванием

a <<= b

__ilshift__(a, b)

Сдвиг вправо с присваиванием

a >>= b

__irshift__(a, b)

Деление по модулю с присваиванием

a %= b

__imod__(a, b)

Умножение с присваиванием

a += b

__imul__(a, b)

Умножение матриц с присваиванием

a @= b

__imatmul__(a, b)

Поразрядное сложение с присваиванием

a |= b

__ior__(a, b)

Возведение в степень с присваиванием

a **= b

__ipow__(a, b)

Вычитание с присваиванием

a -= b

__isub__(a, b)

Деление с присваиванием

a /= b

__itruediv__(a, b)

Операция XOR с присваиванием

a ^= b

__ixor__(a, b)

Чтобы определить оператор для некоторого класса, данный класс должен реализовать соответствующую функцию. Так, для определения оператора сложения применяется функция __add__(), поэтому внутри класса нам надо определить данную функцию. Например:

class Counter:
    def __init__(self, value):
        self.value = value
    # переопределение оператора сложения
    def __add__(self, other):
        return Counter(self.value + other.value)
    
counter1 = Counter(5)
counter2 = Counter(15)
counter3 = counter1 + counter2
print(counter3.value)       # 20

Здесь определен класс Counter, который имеет атрибут value - условное некоторое число. С помощью функции __add__ определяем для типа Counter оператор сложения. Допустим, мы хотим, чтобы один объект Counter можно было сложить с другим объектом Counter. В этом случае второй параметр функции будет представлять другой объект Counter:

def __add__(self, other):
    return Counter(self.value + other.value)

В результате возвращаем новый объект Counter, в который помещается сумма атрибутов value обоих объектов.

После этого мы сможем складывать два объекта Counter, и результатом сложения будет новый объект Counter.

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

class Counter:
    def __init__(self, value):
        self.value = value
        
    def __add__(self, other):
        return Counter(self.value + other)
    
counter1 = Counter(5)
counter3 = counter1 + 6
print(counter3.value)       # 11

Здесь оператор сложения по прежнему возвращает объект Counter, только теперь второй параметр представляет обычное число.

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

class Counter:
    def __init__(self, value):
        self.value = value
        
    def __add__(self, other):
        return self.value + other
    
counter1 = Counter(5)
result = counter1 + 7
print(result)       # 12

Рассмотрим еще ряд примеров определения операторов.

Истинность объекта

Определение функции __bool__ позволяет установить истинность объекта или фактически преобразовать объект к значениям True/False. Например:

class Counter:
    def __init__(self, value):
        self.value = value
    def __bool__(self):
        return self.value > 0
    
def test(counter):
    if counter: print("Counter = True")
    else: print("Counter = False")
    
counter1 = Counter(3)
test(counter1)              # Counter = True

counter2 = Counter(-3)
test(counter2)              # Counter = False

В данном случае будем считать, что, если значение value в Counter меньше 1, то объект Counter будет рассматриваться как False, а при положительных значениях - как True. Благодаря этому мы можем использовать объект Counter в условных или циклических конструкциях. Так, в примере выше для тестирования определена функция test, которая в конструкции if..else проверяет значение объекта Counter и в зависимости от результата проверки выводит определенное сообщение на консоль.

Или, например, мы могли бы использовать объект Counter в цикле while в качестве условия:

class Counter:
    def __init__(self, value):
        self.value = value
    def __bool__(self):
        return self.value > 0
    
counter1 = Counter(3)

while(counter1):
    print("Counter1: ", counter1.value)
    counter1.value -=1

В данном случае цикл while будет выполняться, пока counter1 соответствует значению True (по сути покак его значение value больше 0)

Операторы, которые возвращают значение bool

Ряд операций призваны возвращать логическое значение True или False. Например, операции сравнения:

class Counter:
    def __init__(self, value):
        self.value = value
        
    def __gt__(self, other):
        return self.value > other.value
    def __lt__(self, other):
        return self.value < other.value

    
counter1 = Counter(1)
counter2 = Counter(2)
    
if counter1 > counter2: 
    print("counter1 больше чем counter2")
elif counter1 < counter2:
    print("counter1 меньше чем counter2")
else: 
    print("counter1 и counter2 равны")

Здесь в классе Counter определены операторы < (функция __lt__()) и > (функция __gt__()). В данном случае сравниваем с другим объектом Counter. В реальности же сравниваем значения атрибутов двух объектов.

def __gt__(self, other):
    return self.value > other.value
def __lt__(self, other):
    return self.value < other.value

Затем мы можем применять соответствующие операции к двум объектам Counter:

if counter1 > counter2: 

Операции обращения по индексу

Ряд операторов позволяют обращаться к объекту по индексу, используя квадратные скобки:

obj[index]

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

class Person:
    def __init__(self, name, age):
        self.__name = name
        self.__age = age
        
    def __getitem__(self, prop):
        if prop == "name": return self.__name
        elif prop == "age": return self.__age
        return None
    
tom = Person("Tom", 39)

print("Name:", tom["name"])     # Name: Tom
print("Age:", tom["age"])       # Age: 39
print("Id:", tom["id"])         # Id: None

Итак, здесь определен класс Person, содержит два приватных поля - __name и __age. Для реализации получения данных по индексу определена функция __getitem__(). В качестве второго параметра эту функцию передается значение, которое выполняет роль индекса. В нашем случае это будет название атрибута. И в зависимости от переданного значения возвращаем либо значение атрибута __name, либо значение атрибута __age. Если передано невалидное названия атрибута, то возвращаем None.

Для получения значения по индесу передаем индекс - название атрибута в квадратных скобках:

tom["name"]

Проверка наличия свойства

Оператор in позволяет проверить наличие определенного значения в последовательности - некотором наборе значений:

значение in последовательность

Если значение присутствует в последовательности, то возвращается True, иначе возвращается False. Например, проверим наличие свойства в объекте:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
    def __contains__(self, prop):
        if prop == "name" or prop == "age": return True 
        return False
    
tom = Person("Tom", 39)
print("name" in tom)        # True
print("id" in tom)          # False

За реализацию оператора in отвечает функция __contains__(). В качестве первого параметра, как обычно, указывается текущий объект - тот объект, который стоит справа от оператора in. А в качестве второго параметра - проверяемое значение - оно указывается слева от in. В данном случае если второй параметр - равен "name" или "age", то возвращаем True. Что означает, что атрибут есть в объекте.

Соответственно выражение "name" in tom возвратит True, а выражение "id" in tom возвратит False.

Реализация операторов парами

Некоторые операторы - операторы сравения удобнее реализовать парами. Если мы реализуем оператор ==, то можно сразу реализовать и оператор !=. Причем чтобы не прописывать одну и ту же логику по два раза, можно реализовать один оператор через другой:

class Counter:
    def __init__(self, value):
        self.value = value
    
    def __eq__(self, other): return self.value == other.value
    def __ne__(self, other): return not (self == other)
    
    def __gt__(self, other): return self.value > other.value
    def __le__(self, other): return not (self > other)
    
    def __lt__(self, other): return self.value < other.value
    def __ge__(self, other): return not (self < other)
        
c1 = Counter(1)
c2 = Counter(2)

print(c1 == c2)     # False
print(c1 != c2)     # True

print(c1 < c2)      # True
print(c1 >= c2)     # False

В данном случае оператора != возвращает инверсию результата оператора ==, который определен выше

def __eq__(self, other): return self.value == other.value
def __ne__(self, other): return not (self == other)

Аналогично определены операторы < и >=, а также > и <=.

Дополнительные материалы
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850