Полиморфизм – это возможность работы с совершенно разными объектами (языка Python) единым образом. (т.е. через единый интерфейс)
Разберемся на примере: пусть есть два класса: для прямоугольников и квадратов:
class Rectangle:
def __init__(self, w, h):
self.w = w
self.h = h
def get_rect_pr(self):
return 2 * (self.w + self.h)
class Square:
def __init__(self, a):
self.a = a
def get_sq_pr(self):
return 4 * self.a
r1 = Rectangle(1, 2)
r2 = Rectangle(3, 4)
sq1 = Square(10)
sq2 = Square(20)
Получить периметры очевидно можем так:
print(r1.get_rect_pr(), r2.get_rect_pr())
print(sq1.get_sq_pr(), sq2.get_sq_pr())
Но... Если захотим перебрать элементы некого списка объектов [r1, r2, sq1, sq2] и через цикл получить значение периметра, то это будет проблематичнее, ведь у них разные методы для получения данного значения. Можно, конечно, сделать проверку на принадлежность объекта какому-то классу и потом уже вызывать соответствующий метод.
Например так:
geom = [r1, r2, sq1, sq2]
for g in geom:
if isinstance(g, Rectangle):
print(g.get_rect_pr())
else:
print(g.get_sq_pr())
А если еще один или несколько классов добавиться, что тогда делать? Добавлять каждый раз ветку и плодить 100500 условий? В данном случае код получается не очень гибким.
class Triangle:
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
def get_tr_pr(self):
return self.a + self.b + self.c
Это можно исправить. Вот как раз здесь очень хорошо применим подход, который и называется полиморфизмом. Мы договоримся в каждом классе создавать методы с одинаковыми именами, например - get_pr(). Получается что к разным объектам мы обращаемся через единый интерфейс - это и есть полиморфизм.
Но можно еще немного улучшить эту методику.
Для чего? А в друг мы забудем где-то создать метод для получения периметра. Я, например, могу что-то забыть вполне спокойно😬 Тут есть два пути решения проблемы. Сначала создаем базовый класс а остальным - унаследоваться, затем:
1 способ) В нем прописать метод get_pr().
def get_pr(self):
return -1
2 способ) Реализовать через абстрактный метод
def get_pr(self):
raise NotImplementedError(f"В дочернем классе: {self.__class__.__name__} должен быть переопределен метод get_pr()")
)
• В 1 варианте мы не будем получать ошибок, при вызове - да. Но результат будет не тот, который ожидаем.
• Во 2 варианте будет выдаваться ошибка, которая конкретно указывает что мы забыли переопределить метод в дочернем классе.
Итоговая реализация выглядит вот так:
class Geom:
def get_pr(self):
raise NotImplementedError(f"В дочернем классе: {self.__class__.__name__} должен быть переопределен метод get_pr()")
class Rectangle(Geom):
...
def get_pr(self):
return 2 * (self.w + self.h)
...
class Square(Geom):
...
class Triangle(Geom):
...
Подытожим. В языках программирования методы, которые обязательно нужно переопределять в дочерних классах и которые не имеют своей собственной реализации называют абстрактными.
Вы можете заметить, что есть еще абстрактные классы и абстрактные методы. Но об этом в другой раз😏
Комментарии и реакции приветствуются👇
🔥 — Огонь, приму на вооружение!
🤔 — Знал, но освежил память.
💬 — Есть вопросы в комментарии!