Полиморфизм

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

Например, два разных класса содержат метод total, однако инструкции каждого предусматривают совершенно разные операции. Так в классе T1 – это прибавление 10 к аргументу, в T2 – подсчет длины строки символов. В зависимости от того, к объекту какого класса применяется метод total, выполняются те или иные инструкции.

class T1:
     n=10
     def total(self, N):
          self.total = int(self.n) + int(N)
 
class T2:
     def total(self,s):
          self.total = len(str(s))
 
t1 = T1()
t2 = T2()
t1.total(45)
t2.total(45)
print(t1.total) # Вывод: 55
print(t2.total) # Вывод: 2

В предыдущем уроке мы уже наблюдали полиморфизм между классами, связанными наследованием. У каждого может быть свой метод __init__() или square() или какой-нибудь другой. Какой именно из методов square() вызывается, и что он делает, зависит от принадлежности объекта к тому или иному классу.

Однако классы не обязательно должны быть связанны наследованием. Полиморфизм как один из ключевых элементов ООП существует независимо от наследования. Классы могут быть не родственными, но иметь одинаковые методы, как в примере выше.

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

В Python среди прочего полиморфизм находит отражение в методах перегрузки операторов. Два из них мы уже рассмотрели. Это __init__() и __del__(), которые вызываются при создании объекта и его удалении. Полиморфизм у методов перегрузки операторов проявляется в том, что независимо от типа объекта, его участие в определенной операции, вызывает метод с конкретным именем. В случае __init()__ операцией является создание объекта.

Рассмотрим пример полиморфизма на еще одном методе, который перегружает функцию print().

Если вы создадите объект собственного класса, а потом попробуете вывести его на экран, то получите информацию о классе объекта и его адрес в памяти. Такое поведение функции print() по-умолчанию по отношению к пользовательским классам запрограммировано на самом верхнем уровне иерархии, где-то в суперклассе, от которого неявно наследуются все остальные.

>>> class A:
...     def __init__(self, v1, v2):
...             self.field1 = v1
...             self.field2 = v2
... 
>>> a = A(3, 4)
>>> print(a)
<__main__.A object at 0x7f840c8acfd0>

Если же мы хотим, чтобы, когда объект передается функции print(), выводилась какая-нибудь другая более полезная информация, то в класс надо добавить специальный метод __str__(). Этот метод должен обязательно возвращать строку, которую будет выводить функция print():

class A:
    def __init__(self, v1, v2):
        self.field1 = v1
        self.field2 = v2
    def __str__(self):
        return str(self.field1) + " " + str(self.field2)
 
a = A(3, 4)
print(a)

Вывод:

3 4

Какую именно строку возвращает метод __str__(), дело десятое. Он вполне может строить квадратик из символов:

class Rectangle:
    def __init__(self, width, height, sign):
        self.w = int(width)
        self.h = int(height)
        self.s = str(sign)
    def __str__(self):
        rect = []
        for i in range(self.h): # количество строк
            rect.append(self.s * self.w) # знак повторяется w раз
        rect = '\n'.join(rect) # превращаем список в строку
        return rect
 
b = Rectangle(10, 3, '*')
print(b)

Вывод:

**********
**********
**********

Практическая работа. Метод перегрузки оператора сложения

В качестве практической работы попробуйте самостоятельно перегрузить оператор сложения. Для его перегрузки используется метод __add__(). Он вызывается, когда объекты класса, имеющего данный метод, фигурируют в операции сложения, причем с левой стороны. Это значит, что в выражении a + b у объекта a должен быть метод __add__(). Объект b может быть чем угодно, но чаще всего он бывает объектом того же класса. Объект b будет автоматически передаваться в метод __add__() в качестве второго аргумента (первый – self).

Отметим, в Python также есть правосторонний метод перегрузки сложения - __radd__().

Согласно полиморфизму ООП, возвращать метод __add__() может что угодно. Может вообще ничего не возвращать, а "молча" вносить изменения в какие-то уже существующие объекты. Допустим, в вашей программе метод перегрузки сложения будет возвращать новый объект того же класса.

Курс с примерами решений практических работ: android-приложение, pdf-версия.

Создано

Обновлено

Комментарии

class Unit(object):
    '''Unit Class'''
    def __init__(self, hp, damage):
        self.hp = hp
        self.damage = damage
 
    def __str__(self):
        return f'Unit. HP: {self.hp}, damage: {self.damage}'
 
    def __add__(self, other):
        new_hp = int((self.hp + other.hp) * 0.5)
        new_damage = int(((self.damage + other.damage) * 0.7))
        return Unit(new_hp, new_damage)
 
 
def main():
    unit_01 = Unit(50, 10)
    unit_02 = Unit(75, 15)
    new_unit = unit_01 + unit_02
 
    print(new_unit)
 
 
if __name__ == '__main__':
    main()

Иногда ввообще не ясно что делать в практическом задании..у обьекта а не может быть метода " __add__ " вызваного напрямую.. как и в предидущем задании смысла нет..но все же:
class A:
    def num(self, x):
        self.a = x
 
    def __add__(self, y):
        self.b = y
        self.s = str(self.a)+str(self.b)
 
    def add(self, y):
        A.__add__(self, y)
        return self.s 
 
q = A()