Добавим атрибут, специфический для электромобилей (например, мощность аккумулятора), и метод для вывода информации об этом атрибуте:
class Car():
...
class ElectricCar(Car):
"""Представляет аспекты машины, специфические для электромобилей."""
def __init__(self, make, model, year):
. . . ."""
. . . .Инициализирует атрибуты класса-родителя.
. . . .Затем инициализирует атрибуты, специфические для электромобиля.
. . . ."""
super().__init__(make, model, year)
(1) . . . .self.battery_size = 70
. . . .
(2) . .def describe_battery(self):
. . . ."""Выводит информацию о мощности аккумулятора."""
. . . .print("This car has a " + str(self.battery_size) + "-kWh battery.")
my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.describe_battery()
В точке (1) добавляется новый атрибут self.battery_size, которому присваивается исходное значение — скажем, 70. Этот атрибут будет присутствовать во всех экземплярах, созданных на основе класса ElectricCar (но не во всяком экземпляре Car). Также добавляется метод с именем describe_battery(), который выводит информацию об аккумуляторе в точке (2). При вызове этого метода выводится описание, которое явно относится только к электромобилям:
2016 Tesla Model S
This car has a 70-kWh battery.
Возможности специализации класса ElectricCar беспредельны. Вы можете добавить сколько угодно атрибутов и методов, чтобы моделировать электромобиль с любой нужной точностью. Атрибуты или методы, которые могут принадлежать любой машине (а не только электромобилю), должны добавляться в класс Car вместо ElectricCar. Тогда эта информация будет доступна всем пользователям класса Car, а класс ElectricCar будет содержать только код информации и поведения, специфических для электромобилей.
Переопределение методов класса-родителя
Любой метод родительского класса, который в моделируемой ситуации делает не то, что нужно, можно переопределить. Для этого в классе-потомке определяется метод с тем же именем, что и у метода класса-родителя. Python игнорирует метод родителя и обращает внимание только на метод, определенный в потомке.
Допустим, в классе Car имеется метод fill_gas_tank(). Для электромобилей заправка бензином бессмысленна, поэтому этот метод логично переопределить. Например, это можно сделать так:
def ElectricCar(Car):
...
. .def fill_gas_tank():
. . . ."""У электромобилей нет бензобака."""
. . . .print("This car doesn't need a gas tank!")
И если кто-то попытается вызвать метод fill_gas_tank() для электромобиля, Python игнорирует метод fill_gas_tank() класса Car и выполнит вместо него этот код. С применением наследования потомок сохраняет те аспекты родителя, которые вам нужны, и переопределяет все ненужное.
Экземпляры как атрибуты
При моделировании явлений реального мира в программах классы нередко дополняются все большим количеством подробностей. Списки атрибутов и методов растут, и через какое-то время файлы становятся длинными и громоздкими. В такой ситуации часть одного класса нередко можно записать в виде отдельного класса. Большой код разбивается на меньшие классы, которые работают во взаимодействии друг с другом.
Например, при дальнейшей доработке класса ElectricCar может оказаться, что в нем появилось слишком много атрибутов и методов, относящихся к аккумулятору. В таком случае можно остановиться и переместить все эти атрибуты и методы в отдельный класс с именем Battery. Затем экземпляр Battery становится атрибутом класса ElectricCar:
class Car():
...
(1) class Battery():
. ."""Простая модель аккумулятора электромобиля."""
(2) . .def __init__(self, battery_size=70):
. . . ."""Инициализирует атрибуты аккумулятора."""
. . . .self.battery_size = battery_size
(3) . .def describe_battery(self):
. . . ."""Выводит информацию о мощности аккумулятора."""
. . . .print("This car has a " + str(self.battery_size) + "-kWh battery.") . .
class ElectricCar(Car):
"""Представляет аспекты машины, специфические для электромобилей."""
def __init__(self, make, model, year):
"""
Инициализирует атрибуты класса-родителя.
Затем инициализирует атрибуты, специфические для электромобиля.
"""
super().__init__(make, model, year)
(4) . . . .self.battery = Battery()
my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
В точке (1) определяется новый класс с именем Battery, который не наследует ни от одного из других классов. Метод __init__() в точке (2) получает один параметр battery_size, кроме self. Если значение не предоставлено, этот необязательный параметр задает battery_size значение 70. Метод describe_battery() также перемещен в этот класс (3).