Нашли ошибку?
Скачать чистый
урок 1 из 4≈ 20 минупражнения: 0 из 3

Наследование

class B(A): подкласс получает атрибуты и методы базового класса, может добавить свои и переопределить чужие. isinstance и issubclass в иерархии и проверки не в том порядке.

Python запустится при первом выполнении

В меню «Кофейной лавки» есть обычные товары, сезонные напитки и выпечка. Всё это товары: у всех название, цена, строка для меню. Но у сезонных напитков своя пометка, а выпечку предлагают в комплекте с кофе. Копировать класс Product три раза и менять по строчке — плохая идея: исправление пришлось бы вносить в три места. Вместо этого классы строят на основе другого класса.

class B(A)

Если в скобках после имени класса указать другой класс, новый класс наследует (inheritance) всё, что есть у того: атрибуты класса и методы, включая __init__. Класс в скобках называют базовым (base class, «родитель»), а новый — подклассом (subclass, «дочерний класс»):

[ ]Подкласс без единой строки
class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def __repr__(self):
        return f"{type(self).__name__}({self.name!r}, {self.price})"

    def label(self):
        return f"{self.name} — {self.price} ₽"


class Snack(Product):
    pass


cookie = Snack("Овсяное печенье", 90)
print(cookie.label())
print(cookie)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
Овсяное печенье — 90 ₽
Snack('Овсяное печенье', 90)

У Snack нет своего тела, кроме pass, но экземпляр создаётся с названием и ценой и умеет label() — всё это пришло из Product. Поиск атрибута продолжается вверх по иерархии: сначала экземпляр, потом его класс, потом базовый класс.

Свои методы и переопределение

Подкласс может добавить методы, которых нет у базового класса, и переопределить (override) существующие — объявить метод с тем же именем. Тогда для экземпляров подкласса работает новая версия, а для экземпляров базового класса — старая:

[ ]Свой метод и своя версия label
class Tea(Product):
    def brew_minutes(self):  # новый метод
        return 4

    def label(self):  # своя версия метода базового класса
        return f"{self.name} — {self.price} ₽, заваривать {self.brew_minutes()} мин"


print(Tea("Улун", 180).label())
print(Product("Улун", 180).label())
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
Улун — 180 ₽, заваривать 4 мин
Улун — 180 ₽
[ ]Упражнение 1Сезонный товар решено

Объявите подкласс SeasonalProduct класса Product из демонстрации. Переопределите в нём label(): к обычной строке добавляется пометка — "Тыквенный латте — 290 ₽ (сезонное)".

class SeasonalProduct:  # укажите базовый класс
    def label(self):
        ...

[ ]Упражнение 2Выпечка к кофе решено

Объявите подкласс Pastry класса Product с новым методом combo_price(drink): цена комплекта «выпечка + напиток» со скидкой 10 %, округлённая до целых рублей. drink — любой товар с атрибутом price.

class Pastry(Product):
    def combo_price(self, drink):
        ...

isinstance и issubclass в иерархии

Экземпляр подкласса — это и экземпляр базового класса: круассан — тоже товар. Поэтому isinstance для него возвращает True с обоими классами. А issubclass(A, B) проверяет классы: построен ли A на основе B:

[ ]Круассан — тоже товар
croissant = Pastry("Круассан", 140)
print(isinstance(croissant, Pastry), isinstance(croissant, Product))
print(isinstance(Product("Чай", 120), Pastry))
print(issubclass(Pastry, Product), issubclass(SeasonalProduct, Pastry))
print(issubclass(bool, int))
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
True True
False
True False
True

Вспомните вопрос из урока «Всё в Python — объект»: isinstance(True, int) — True, потому что bool — подкласс int.

[ ]Все стали «товарами»
def shelf_of(item):
    if isinstance(item, Product):  # ошибка: базовый класс первым
        return "общая полка"
    elif isinstance(item, Pastry):
        return "витрина с выпечкой"
    return "?"


for item in [Product("Чай", 120), Pastry("Круассан", 140)]:
    print(item.name, "→", shelf_of(item))
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
Чай → общая полка
Круассан → общая полка
[ ]Упражнение 3Что это за товар решено

Напишите функцию kind_of(item): она возвращает "сезонное" для SeasonalProduct, "выпечка" для Pastry и "товар" для любого другого Product.

def kind_of(item):
    ...

Вопрос

Что вернёт issubclass(Product, Pastry)?

Наследование хорошо, когда подкласс является разновидностью базового класса: сезонный товар — товар, выпечка — товар. В следующем уроке подкласс научится не заменять метод базового класса целиком, а расширять его.

Нашли ошибку?