- Перегрузка операторов в Python
- Практическая работа
- Перегрузка операторов в Python
- Различные варианты использования основных арифметических операторов
- Как перегрузить оператор в Python?
- Специальные функции в Python
- Для двоичных операторов в Python
- Для операторов сравнения в Python
- Для операторов присваивания
- Для унарных операторов
- Перегрузка бинарного оператора
- Перегрузка операторов сравнения
- Пример
Перегрузка операторов в Python
Перегрузка операторов в Python – это возможность с помощью специальных методов в классах переопределять различные операторы языка. Имена таких методов включают двойное подчеркивание спереди и сзади.
Под операторами в данном контексте понимаются не только знаки +, -, *, /, обеспечивающие операции сложения, вычитания и др., но также специфика синтаксиса языка, обеспечивающая операции создания объекта, вызова объекта как функции, обращение к элементу объекта по индексу, вывод объекта и другое.
Мы уже использовали ряд методов перегрузки операторов. Это
- __init__() – конструктор объектов класса, вызывается при создании объектов
- __del__() – деструктор объектов класса, вызывается при удалении объектов
- __str__() – преобразование объекта к строковому представлению, вызывается, когда объект передается функциям print() и str()
- __add__() – метод перегрузки оператора сложения, вызывается, когда объект участвует в операции сложения будучи операндом с левой стороны
- __setattr__() – вызывается, когда атрибуту объекта выполняется присваивание
В Python много других методов перегрузки операторов. В этом уроке рассмотрим еще несколько.
На самом деле перегрузка большинства операторов в пользовательских классах используется не так часто. Но сам факт наличия такой особенности объектно-ориентированного программирования требует отдельного рассмотрения темы.
Возможность перегрузки операторов обеспечивает схожесть пользовательского класса со встроенными классами Python. Ведь все встроенные типы данных Питона – это классы. В результате все объекты могут иметь одинаковые интерфейсы. Так если ваш класс предполагает обращение к элементу объекта по индексу, например a[0] , то это можно обеспечить.
Пусть будет класс-агрегат B , содержащий в списке объекты класса A :
class A: def __init__(self, arg): self.arg = arg def __str__(self): return str(self.arg) class B: def __init__(self, *args): self.aList = [] for i in args: self.aList.append(A(i)) group = B(5, 10, 'abc')
Чтобы получить элемент списка, несомненно, мы можем обратиться по индексу к полю aList :
Однако куда интереснее извлекать элемент по индексу из самого объекта, а не из его поля:
class B: def __init__(self, *args): self.aList = [] for i in args: self.aList.append(A(i)) def __getitem__(self, i): return self.aList[i] group = B(5, 10, 'abc') print(group.aList[1]) # выведет 10 print(group[0]) # 5 print(group[2]) # abc
Это делает объекты класса B похожими на объекты встроенных в Python классов-последовательностей (списков, строк, кортежей). Здесь метод __getitem__ перегружает операцию извлечения элемента по индексу. Другими словами, этот метод вызывается, когда к объекту применяется операция извлечения элемента: объект[индекс] .
Бывает необходимо, чтобы объект вел себя как функция. Это значит, если у нас есть объект a , то мы можем обращаться к нему в нотации функции, то есть ставить после него круглые скобки и даже передавать в них аргументы:
Метод __call__ автоматически вызывается, когда к объекту обращаются как к функции. Например, здесь во второй строке произойдет вызов метода __call__() некогоКласса :
объект = некийКласс() объект([возможные аргументы])
class Changeable: def __init__(self, color): self.color = color def __call__(self, newcolor): self.color = newcolor def __str__(self): return "%s" % self.color canvas = Changeable("green") frame = Changeable("blue") canvas("red") frame("yellow") print(canvas, frame)
В этом примере с помощью конструктора класса при создании объектов устанавливается их цвет. Если требуется его поменять, то достаточно обратиться к объекту как к функции и в качестве аргумента передать новый цвет. Такой обращение автоматически вызовет метод __call__() , который, в данном случае, изменит атрибут color объекта.
В Python кроме метода __str__ есть схожий с ним по поведению, но более «низкоуровневый» метод __repr__ . Оба метода должны возвращать строку.
Если в классе есть только метод __str__() , то при обращении к объекту в интерпретаторе без функции print() , он не будет вызываться:
>>> class A: . def __str__(self): . return "This is object of A" . >>> a = A() >>> print(a) This is object of A >>> a >>> str(a) 'This is object of A' >>> repr(a) ''
Метод __str__() вызывается при попытке преобразования объекта в строку с помощью встроенной функции str() . А функция print() так устроена, что сама вызывает str() для своих аргументов.
В Python есть встроенная функция repr() , которая также как str() преобразует объект в строку. Но «сырую» строку. Что это значит, попробуем понять с помощью примера:
>>> a = '3 + 2' >>> b = repr(a) >>> a '3 + 2' >>> b "'3 + 2'" >>> eval(a) 5 >>> eval(b) '3 + 2' >>> c = "Hello\nWorld" >>> d = repr(c) >>> c 'Hello\nWorld' >>> d "'Hello\\nWorld'" >>> print(c) Hello World >>> print(d) 'Hello\nWorld'
Функция eval() преобразует переданную строку в программный код, который тут же выполняется. Функция print() выполняет переход на новую строку, если встречает символ \n . Функция repr() выполняет действия, направленные на своего рода защиту строки от интерпретации, оставляет ее «сырой», т. е. в исходном виде. Еще раз:
>>> c = "Hello\nWorld" >>> c # аналог print(repr(c)) 'Hello\nWorld' >>> print(c) # аналог print(str(c)) Hello World
Если для вашего класса различия между сырым и обычным строковым представлением экземпляров не важны, желательно определять только метод __repr__ . Он будет вызываться для всех случаев преобразования к строке:
>>> class A: . def __repr__(self): . return "It's obj of A" . >>> a = A() >>> a It's obj of A >>> repr(a) "It's obj of A" >>> str(a) "It's obj of A" >>> print(a) It's obj of A
Если же нужен различающийся вывод данных, тогда в классе следует определить оба метода перегрузки операторов преобразования к строке.
Практическая работа
Напишите класс Snow по следующему описанию.
В конструкторе класса инициируется поле, содержащее количество снежинок, выраженное целым числом.
Класс включает методы перегрузки арифметических операторов: __add__ – сложение, __sub__ – вычитание, __mul__ – умножение, __truediv__ – деление. В классе код этих методов должен выполнять увеличение или уменьшение количества снежинок на число n или в n раз. Метод __truediv__ перегружает обычное / , а не целочисленное // деление. Однако пусть в методе происходит округление значения до целого числа.
Класс включает метод make_snow , который помимо self принимает число снежинок в ряду и возвращает строку вида «*****\n*****\n*****…» , где количество снежинок между ‘\n’ равно переданному аргументу, а количество рядов вычисляется, исходя из общего количества снежинок.
Вызов экземпляра класса Snow в нотации функции с одним аргументом, должен приводить к перезаписи значения поля, в котором хранится количество снежинок, на переданное в качестве аргумента значение.
Курс с примерами решений практических работ:
pdf-версия
Объектно-ориентированное программирование на Python
Перегрузка операторов в Python
Перегрузка оператора — это явление придания альтернативного значения действию, выполняемому оператором за пределами их предопределенной операционной функции. Перегрузка оператора также называется специализированным полиморфизмом оператора.
Операторы Python работают для встроенных классов. Но один и тот же оператор по-разному выражает разные типы. Например, оператор + выполнит арифметическое сложение двух чисел, объединит два списка и объединит две строки. Python позволяет одному и тому же оператору иметь разные значения в зависимости от контекста ссылки.
Различные варианты использования основных арифметических операторов
# Program to show use of # + operator for different purposes. print(5 + 5) # concatenate two strings print("Safa"+"Mulani") # Product two numbers print(10 * 10) # Repeat the String print("Safa"*4)
10 SafaMulani 100 SafaSafaSafaSafa
Как перегрузить оператор в Python?
Для выполнения перегрузки оператора Python предоставляет некоторую специальную функцию, которая автоматически вызывается, когда она связана с этим конкретным оператором. Например, когда мы используем оператор +, автоматически вызывается метод __add__ , в котором определяется операция для оператора +.
Специальные функции в Python
Глобальные функции, начинающиеся с двойного подчеркивания __, в Python называются специальными функциями. Это потому, что они необычные. Одна из них — функция __init __(), которую мы обычно определяем и напоминаем как конструктор. Он вызывается каждый раз, когда мы создаем новый объект этого класса.
Для двоичных операторов в Python
Оператор | Метод |
---|---|
+ | __add__(self, other) |
– | __sub__(self, other) |
* | __mul__(self, other) |
/ | __truediv__(self, other) |
// | __floordiv__(self, other) |
% | __mod__(self, other) |
** | __pow__(self, other) |
Для операторов сравнения в Python
Оператор | Метод |
---|---|
__lt__(self, other) | |
> | __gt__(self, other) |
__le__(self, other) | |
>= | __ge__(self, other) |
== | __eq__(self, other) |
!= | __ne__(self, other) |
Для операторов присваивания
Оператор | Метод |
---|---|
-= | __isub__(self, other) |
+= | __iadd__(self, other) |
*= | __imul__(self, other) |
/= | __idiv__(self, other) |
//= | __ifloordiv__(self, other) |
%= | __imod__(self, other) |
**= | __ipow__(self, other) |
Для унарных операторов
Перегрузка бинарного оператора
Когда мы используем оператор +, автоматически вызывается волшебный метод __add__ , в котором определяется операция для оператора +. Следовательно, изменяя код метода, мы можем придать альтернативное значение оператору +.
# Program to overload an binary + operator class X: def __init__(self, x): self.x = x # adding two objects def __add__(self, y): return self.x + y.x ob1 = X(5) ob2 = X(5) ob3 = X("Safa") ob4 = X("Mulani") print(ob1 + ob2) # simple addition of objects print(ob3 + ob4) # concatenation of strings through object addition
Перегрузка операторов сравнения
class X: def __init__(self, x): self.x = x def __lt__(self, other): # Overloading < operator if(self.x ob1 is less than ob2 Not equalПример
class Animal: def __init__(self, age): self.__age = age def setage(self, age): self.__age = age def getage(self): return self.__age def __add__(self, predict): return Animal( self.__age + predict.__age ) def __gt__(self, predict): return self.__age > predict.__age def __lt__(self, predict): return self.__age < predict.__age def __str__(self): return "Animal with original age " + str(self.__age) c1 = Animal(5) print(c1.getage()) c2 = Animal(5) print(c2.getage()) c3 = c1 + c2 print(c3.getage()) print( c3 >c2) print( c1 < c2) print(c3)5 5 10 True False Animal with original age 10