Три принципа ООП: наследование и полиморфизм
Наследование
Наследование позволяет создать новый класс на основе уже существующего (т.е. унаследовать от его). Базовый класс (тот, который мы расширяем) называется родителем (parent), а новый - наследником (child). Пример этого явления - ниже.
import random
class Dog:
"""Описывает собаку, которая умеет лаять в зависимости от голода.
Голод выбирается случайным образом"""
def __init__(self):
# свойство голод - защищенное - оно недоступно извне, но доступно наследникам
self._hunger = random.randint(1, 5)
def voice(self):
"""Чем голоднее собака, тем дольше лает!"""
print("wow " * self._hunger)
# Наследуем кошку от собаки, в наследство кошка получает метод voice
class Cat(Dog):
"""Описывает кошку. Помимо голода у нее есть милота."""
def __init__(self):
self._hunger = random.randint(1, 5)
self._cuttiness = random.randint(1, 5)
if __name__ == "__main__":
c = Cat()
c.voice()
Если вам кажется, что что-то не так, вы правы! Обязательно смотрите следующую страницу!
Наследование - это один из отличных способов повторного использования кода. Ранее мы встречались с этим явлением, когда использовали функции. Функции позволяли сократить код, избавляясь от однотипных операций. Используя наследование можно точно так же избавиться от похожих частей в разных классах.
Полиморфизм
Полиморфизм позволяет наследнику изменять поведение родителя. Иными словами, можно пересоздавать методы родителя в наследнике и это не будет ошибкой. Причем код родителя можно повторно использовать, "добавив" к нему что-то новое.
В предыдущем примере кошке в наследство от родителя досталась функция голос, которая работает не так, как хотелось бы - кошка лает!!! А еще у нас есть фактологическая ошибка: кошка - это никак не подвил собаки, это другое животное. Но интерфейс (набор методов) у них правда одинаковый. Давайте исправим это!
import random
import pytest
class Animal():
"""Описывает абстрактный класс Животное, может лаять и обладает уровнем голода."""
# То что класс абстрактный, означает, что небывает просто животных, бывают только конкретные.
def __init__(self):
self._hunger = random.randint(1, 5)
def voice(self):
raise NotImplementedError
# Собака наследует от животного, реализует его интерфейс
class Dog(Animal):
"""Описывает собаку - животное, которое умеет лаять."""
def __init__(self):
# Собаке нужен только голод, поэтому вызываем конструктор родителя - животного.
super().__init__()
def voice(self):
"""Чем голоднее собака, тем дольше лает!"""
print("wow " * self._hunger)
# Кошка тоже наследует от животного, реализует его интерфейс
class Cat(Animal):
"""Описывает кошку. Помимо голода у нее есть милота и она умеет мяукать."""
def __init__(self):
# вызываем конструктор родителя и добавляем свойство милота
super().__init__()
self._cuttiness = random.randint(1, 5)
def voice(self):
"""Чем голоднее собака, тем дольше лает!"""
print(("m" + ("e" * self._cuttiness) + "ow") * self._hunger)
if __name__ == "__main__":
sharik = Dog()
sharik.voice()
barsik = Cat()
barsik.voice()
with pytest.raises(TypeError):
# животное создать нельзя
strange_entity = Animal()
Результат:
wow wow wow wow
meow meow meow meow meow