Приветствую всех
на еще одном внеочередном занятии по ООП. И здесь я хочу затронуть еще один
вопрос, который волнует моих подписчиков: что такое полиморфизм в ООП на Python? Если
попытаться ответить в двух словах на этот вопрос, то можно было бы сказать так:
Это возможность работы с совершенно разными объектами языка Python единым образом.
Кажется, не
особо стало понятнее? Поэтому давайте, как всегда, постигнем суть этого подхода
на конкретном примере.
Вначале я
продемонстрирую пример, где мы увидим один недостаток, который как раз
исправляется с помощью полиморфизма. Предположим, у нас есть два класса Rectangle и Square:
class Rectangle:
def __init__(self, w, h):
self.w = w
self.h = h
def getPerRect(self):
return 2*(self.w+self.h)
class Square:
def __init__(self, a):
self.a = a
def getPerSq(self):
return 4*self.a
И в них
объявлены геттеры getPerRect и getPerSq для получения
периметра соответствующих фигур: прямоугольника и квадрата. Далее, мы можем
создать экземпляры этих классов и вывести в консоль значения периметров:
r1 = Rectangle(1, 2)
r2 = Rectangle(3, 4)
print(r1.getPerRect(), r2.getPerRect())
s1 = Square(10)
s2 = Square(20)
print(s1.getPerSq(), s2.getPerSq())
Все отлично, все
работает. Но, теперь предположим, что все эти объекты помещаются в коллекцию:
которую можно
легко перебрать с помощью цикла for и где бы мы хотели получить значение
периметра для каждой фигуры:
for g in geom:
print( g.getPerRect() )
Как вы
понимаете, когда в цикле очередь дойдет до объекта s1, возникнет
ошибка, т.к. в классе Square отсутствует метод getPerRect. Конечно, зная,
что в коллекции находятся объекты Rectangle и Square, можно было бы
в цикле записать проверку:
for g in geom:
if isinstance(g, Rectangle):
print( g.getPerRect() )
else:
print(g.getPerSq())
и все
заработает. Но у такого кода мало гибкости и, например, при добавлении еще
одного класса:
class Triangle:
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
def getPerTr(self):
return self.a + self.b + self.c
Получим снова
ошибку:
t1 = Triangle(1,2,3)
t2 = Triangle(4,5,6)
geom = [r1, r2, s1, s2, t1, t2]
Конечно, в цикле
for можно
дополнительно проверить на соответствие классам Square и Triangle, но красоты и
гибкости нашей программе это не придаст. Вот как раз здесь очень хорошо
применим подход, который и называется полиморфизмом. Мы договоримся в каждом
классе создавать методы с одинаковыми именами, например,
getPerimetr
Тогда в цикле
будем просто обращаться к этому методу и получать периметры соответствующих
фигур:
for g in geom:
print( g.getPerimetr() )
То есть, Python «понимает»
объект какого класса хранится в коллекции и соответственно из этого класса
берет метод getPerimetr. Это и есть полиморфизм в действии, когда к разным
объектам происходит обращение по одному и тому же имени метода и на выходе
получаем разное поведение этой функции.