Python. Наследование классов
Время чтения: 4 минут
Эта статья является составной частью статьи про ООП – если ты хочешь понять всю картину, то лучше начать с неё.
Наследование – позволяет связать разные классы в иерархию таким образом, что мы можем в старших классах определить базовые функции, и переиспользовать их во всех классах наследниках.
Вот пример наследования:
У нас есть базовый класс Seat:
class Seat:
def __init__(self, seat):
self.seat = seat
def take_a_seat(self):
desc = self.get_description()
print(f'Ты сел на {desc}')
def get_description(self):
return f'сиденье из {self.seat}'
В классе Seat есть:
- Поле seat – строка, с названием материала сиденья
- Метод
take_a_seat
– метод, который выводит строку, с описанием на что мы сели - Метод
get_description
– метод, который возвращает строку с описанием того, на что мы сели
Круто, но на сиденьи не получится нормально посидеть – введём класс Tabouret, отнаследованный от класса Seat:
# В скобочках указываем родительский класс Seat
class Tabouret(Seat):
def __init__(self, seat, legs):
Seat.__init__(self, seat)
self.legs = legs
# Переопределяем родительский метод get_description
def get_description(self):
return f'табурет у которого {self.legs} ножки, а сиденье из {self.seat}'
Обрати внимание, что мы вызываем конструктор базового класса:
Seat.__init__(self, seat)
Туда надо передать self (текущий объект) и все параметры, необходимые для этого конструктора. Если не вызвать конструктор базового класса, то он не вызовется и никакие поля базового класса не заполнятся.
Класс Tabouret:
- Добавляет поле legs – число ножек
- Переопределяет метод get_description – теперь, если у объекта класса Tabouret вызвать метод get_description, то вызовется его реализация из класса Tabouret, а не из класса Seat
- Поле seat и метод take_a_seat наследуются из класса Seat
Переопределение метода родительского класса это тоже важное понятие – запомни его.
А как сделать стул? Введём класс Chair и отнаследуем от класса Tabouret:
class Chair(Tabouret):
def __init__(self, seat, legs, back):
Tabouret.__init__(self, seat, legs)
self.back = back
def get_description(self):
return f'стул у которого {self.legs} ножки, сиденье из {self.seat}, а спинка из {self.back}'
Класс Chair:
- Добавляет поле _back – строка, с названием материала спинки
- Переопределяет метод get_description – теперь, если у объекта класса Chair вызвать метод get_description, то вызовется его реализация из класса Chair, а не из класса Tabouret
- Поле legs наследуются из класса Tabouret
- Поле seat и метод take_a_seat наследуются из класса Seat
А что с диваном? Введём класс Couch и отнаследуем его от класса Seat:
class Couch(Seat):
def __init__(self, seat):
Seat.__init__(self, seat)
def get_description(self):
return f'диван из {self.seat}'
Классы Couch и Chair имеют общий родительский класс Seat, но в остальном обладают разными свойствами.
Класс Couch:
- Переопределяет метод get_description – теперь, если у объекта класса Couch вызвать метод get_description, то вызовется его реализация из класса Couch, а не из класса Seat
Давай теперь создадим объекты каждого класса, и попробуем дёрнуть их методы take_a_seat:
seat = Seat('паралона')
tabouret = Tabouret('металла', 3)
chair = Chair('дерева', 4, 'дерева')
couch = Couch('экокожи')
seat.take_a_seat()
# Вывод: Ты сел на сиденье из паралона
tabouret.take_a_seat()
# Вывод: Ты сел на табурет у которого 3 ножки, а сиденье из металла
chair.take_a_seat()
# Вывод: Ты сел на стул у которого 4 ножки, сиденье из дерева, а спинка из дерева
couch.take_a_seat()
# Вывод: Ты сел на диван из экокожи
Окей, а зачем мы это всё сделали?
Тебе не надо дублировать одинаковую логику в разных классах – логика пишется один раз в родительском классе, и переиспользуется во всех дочерних классах.
Например, я захочу поменять вид строки в take_a_seat – для этого я залезу в код класса Seat:
def take_a_seat(self):
desc = self.get_description()
print(f'Ты испытываешь нестерпимое желание сесть на {desc}')
И, не меняя ничего другого, у меня изменится вывод для всех других классов:
seat.take_a_seat()
# Вывод: Ты испытываешь нестерпимое желание сесть на сиденье из паралона
tabouret.take_a_seat()
# Вывод: Ты испытываешь нестерпимое желание сесть на табурет у которого 3 ножки, а сиденье из металла
chair.take_a_seat()
# Вывод: Ты испытываешь нестерпимое желание сесть на стул у которого 4 ножки, сиденье из дерева, а спинка из дерева
couch.take_a_seat()
# Вывод: Ты испытываешь нестерпимое желание сесть на диван из экокожи
Это очень удобно, если работаешь со множеством классов.
Заключение
Итого, мы изучили:
- Наследование
- Родительский класс
- Дочерний класс
- Конструктор родительского класса
- Переопределение метода
И, ещё раз, смысл наследования:
Тебе не надо дублировать одинаковую логику в разных классах – логика пишется один раз в родительском классе, и переиспользуется во всех дочерних классах.
Если что – пиши, я помогу и постараюсь объяснить лучше.
Если ты ещё полон сил, то вернись к статье про ООП, и продолжи постижение ООП.