Занятие №19:

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

Основные понятия 

Определение

Перегрузка операторов -  изменение логики работы различных операторов языка с использованием специальных методов. Эти методы идентифицируются двойным подчеркиванием до и после имени метода.

Под операторами имеются в виду знаки ​ +​, ​-​, ​*​, ​/​, отвечающие за выполнение привычных математических операций, а также особенности синтаксиса языка, обеспечивающие создание объекта, вызова его как функции, получение доступа к элементу объекта по индексу и т. д. К перегружаемым операторам также относятся ​ >​, ​ <​, ​ ≤​, ​ ≥​, ​ ==​, ​ !=​, ​ +=​, ​ -=​.

Простой пример

class Number:
    def __init__(self, value):
        self.data = value
        
    def __add__(self, other):
        return Number(self.data + other)


a = Number(12)
b = a + 4
b.data
16

Некоторые методы перегрузки операций

Метод Что реализует Зачем вызывается 
__init__ Конструктор Создание объекта
a = Class(args)
__del__ Деструктор Уничтожение объекта
__add__ Операция + a + b
__sub__ Операция - a - b
__mul__ Операция * a * b
__getitem__ Индексирование, срезы X[i], X[i:j]
__setitem__ Присваивание по индексу X[i] = value, X[i:j] = value
__len__ Длина len(X)
__bool__ Булевские проверки bool(X)

Внутреннее устройство операций

Если есть встроенная функция func() и соответствующий специальный метод для функции __func__(), Python интерпретирует вызов функции как obj.__func__(), где obj - это объект. В случае с операторами, если у вас есть оператор opr и соответствующий специальный метод для него __opr__(), Python интерпретирует выражение obj1 <opr> obj2 как obj1.__opr__ (obj2) .

Пример

class StarSystem:
    def __init__(self, planets, name):
        self.planets = list(planets)
        self.name = name
        
    def __len__(self):
        return len(self.planets)
        
    def __add__(self, other):
        planets_1 = self.planets.copy()
        planets_1.append(other)
        return StarSystem(planets_1, self.name)

Пример

system = StarSystem(['planet1', 'planet2', 'planet3'], 'StarSystem1')
len(system)
system = system + 'planet4'
system.planets
3
['planet1', 'planet2', 'planet3', 'planet4']

Небольшое замечание

Помните, что не существует преимущества в скорости, если исключить посредника и вызвать метод напрямую.

Список Вызов метода Время
L = list(range (100)) x = L. __ len __ () 0.134
L = list(range (100)) x = len(L) 0.063

Операторы  radd и iadd

class Number1:
    def __init__ (self, val):
        self.val = val
        
    def __add__ (self, other):
        print('add', self.val, other)
        return self.val + other

    # __radd__ = __add__
    
    def __radd__ (self, other):
        print (' radd ', self.val, other)
        return other + self.val
      	# return self.__add__(other)
      	# return self.val + other

Операторы  radd и iadd

x = Number1(32)
y = Number1(25)

x + 1
2 + y
x + y
add 32 1
33
radd 25 2
27
add 32 <__main__.Commuterl object at 0x0000015BF3EE90F0>
radd 25 32
57

Операторы  radd и iadd

class StarSystem:
    def __init__(self, planets, name):
        self.planets = list(planets)
        self.name = name
        
    def __add__(self, other):
        planets_1 = self.planets.copy()
        planets_1.append(other)
        return StarSystem(planets_1, self.name)
      
    def __radd__(self, other):
        planets_1 = self.planets.copy()
        planets_1.insert(0, other)
        return StarSystem(planets_1, self.name)

Операторы  radd и iadd

system_1 = StarSystem(['planet_1'], 'System_1')
system_1 = system_1 + 'planet_2'
system_1.planets

system_1 = 'planet_3' + system_1
system_1.planets
['planet_1', 'planet_2']
['planet_3', 'planet_1', 'planet_2']

Операторы  radd и iadd

class StarSystem:
    def __init__(self, planets, name):
        self.planets = list(planets)
        self.name = name
        
    def __iadd__(self, other):
        self.planets.append(other)
        return self
      
system_1 = StarSystem(['planet_1'], 'System_1')
system_1 += 'planet_2'
system_1.planets
['planet_1', 'planet_2']

Оператор bool

class StarSystem:
    def __init__(self, planets, name):
        self.planets = list(planets)
        self.name = name
        
    def __bool__(self):
        return len(self.planets) > 0
      
system_1 = StarSystem(['planet_1, planet_2'], 'System_1')
system_2 = StarSystem([], 'System_2')

bool(system_1)
bool(system_2)
True
False

Оператор str

class StarSystem:
    def __init__(self, planets, name):
        self.planets = list(planets)
        self.name = name
        
    def __str__(self):
        return f'Название системы {self.name} \
        и её планеты {[i for i in self.planets]}'

Оператор str

system_1 = StarSystem(['planet_1', 'planet_2'], 'System_1')
print(system_1)
Название системы System_1 и её планеты ['planet_1', 'planet_2']

Оператор getitem

class StarSystem:
    def __init__(self, planets, name):
        self.planets = list(planets)
        self.name = name
        
    def __getitem__(self, key):
        return self.planets[key]
      
system_1 = StarSystem(['planet_1', 'planet_2'], 'System_1')
system_1[0]
system_1[0:2]
'planet_1'
['planet_1', 'planet_2']

Правила использования перегрузки операторов

  • Используйте перегрузку при создании своих классов со специальными методами. Также для классов, наследуемых от абстрактных классов;
  • Перегружайте операторы, если это естественно, ожидаемо и не имеет каких-либо побочных эффектов;
  • Не перегружайте операторы просто так. Делайте код максимально удобным, эффективным и читабельным.

Спасибо за понимание!

Лекция №19. Перегрузка операторов

By Protectornaldo

Лекция №19. Перегрузка операторов

  • 84