Перейти к содержанию

232 уроков, 16 библиотек и челлендж «Что выведет код?» — бесплатно, код прямо в браузере

Начать обучение
Урок 10 из 20 Средний 30 мин 130 XP

Абстрактные классы: ABC и @abstractmethod

Абстрактный класс объявляет контракт: какие методы обязан иметь каждый потомок, — и запрещает создавать объекты, пока контракт не выполнен. Разбираем ABC, @abstractmethod и шаблонный метод.

Редакция Питоники

Утиная типизация гибка, но не гарантирует ничего: забыли реализовать метод — AttributeError вылезет в рантайме, далеко от места ошибки. Абстрактные классы объявляют контракт заранее: «каждый потомок обязан уметь cost()». Python проверит это в момент создания объекта — и не даст собрать заведомо сломанный класс.

Контракт без реализации

Абстрактный класс — класс, у которого часть методов объявлена, но не реализована, а создавать его объекты запрещено. Базой служит класс ABC из модуля abc (Abstract Base Classes), декоратор @abstractmethod помечает обязательные методы. Пока потомок не реализует их все, экземпляр не создать и от него.

две доставки — один контракт
from abc import ABC, abstractmethod

class Shipping(ABC):
    @abstractmethod
    def cost(self, weight):
        ...

class CourierShipping(Shipping):
    def cost(self, weight):
        return 300 + weight * 50

class PostShipping(Shipping):
    def cost(self, weight):
        return 150 + weight * 30

for method in [CourierShipping(), PostShipping()]:
    print(method.cost(2))
Вывод
400
210

Shipping.cost описывает, что должна уметь доставка, но не как: тело метода — многоточие, «здесь ничего нет». CourierShipping и PostShipping дают свои формулы, и цикл работает с ними одинаково — тот же полиморфизм из урока 9, но с гарантией. Попробуем теперь создать объект без реализации:

потомок без реализации не создаётся
from abc import ABC, abstractmethod

class Shipping(ABC):
    @abstractmethod
    def cost(self, weight):
        ...

class Pickup(Shipping):
    pass

try:
    Pickup()
except TypeError as error:
    print("Ошибка:", error)
Вывод
Ошибка: Can't instantiate abstract class Pickup without an implementation for abstract method 'cost'

Шаблонный метод: каркас в базовом классе

Настоящая сила ABC — сочетание абстрактных и обычных методов. Базовый класс фиксирует общий алгоритм и вызывает абстрактные шаги, а потомки подставляют только детали. Приём называют шаблонным методом (template method): каркас написан один раз, меняются лишь ступени.

каркас отчёта задаёт база, строки — потомки
from abc import ABC, abstractmethod

class Report(ABC):
    def build(self, rows):
        header = self.header()
        body = "; ".join(self.row(value) for value in rows)
        return f"{header}: {body}"

    @abstractmethod
    def header(self):
        ...

    @abstractmethod
    def row(self, value):
        ...

class SalesReport(Report):
    def header(self):
        return "Продажи"

    def row(self, value):
        return f"{value} руб."

report = SalesReport()
print(report.build([120, 340, 90]))
Вывод
Продажи: 120 руб.; 340 руб.; 90 руб.
МеханизмЧто гарантируетЧего не делает
Утиная типизацияРаботает, если метод есть у объектаНичего не проверяет заранее
Общий родитель без ABCОбщий код и единый типНе заставляет потомка переопределить метод
ABC + @abstractmethodОбъект не создать без реализации всех абстрактных методовНе проверяет типы и число аргументов
Что выведет код?

Сначала предскажи ответ в голове — это главный навык программиста.

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        ...

class Square(Shape):
    def __init__(self, side):
        self.side = side

    def area(self):
        return self.side ** 2

print(Square(5).area())
from abc import ABC, abstractmethod

class Base(ABC):
    @abstractmethod
    def run(self):
        ...

class Lazy(Base):
    pass

try:
    Lazy()
except TypeError as error:
    print("поймали:", type(error).__name__)
from abc import ABC, abstractmethod

class Logger(ABC):
    @abstractmethod
    def write(self, text):
        ...

    def two_lines(self, text):
        return [self.write(text), self.write(text)]

class MemoryLogger(Logger):
    def __init__(self):
        self.lines = []

    def write(self, text):
        self.lines.append(text)
        return "ok"

logger = MemoryLogger()
print(logger.two_lines("старт"))
Проверь себя
0 / 5

1. Что произойдёт при попытке создать экземпляр абстрактного класса?

2. Как объявить обязательный метод в ABC?

3. Когда Python проверяет, что потомок реализовал все абстрактные методы?

4. Может ли абстрактный метод иметь реализацию?

5. Зачем ABC, если duck typing и так работает?

Карточки терминов
Запомнено: 0 / 6
Практика

Создайте абстрактный класс Converter с абстрактным методом convert(celsius). Реализуйте потомков ToFahrenheit (формула c * 1.8 + 32) и ToKelvin (c + 273.15). Пройдитесь циклом по паре «конвертер — подпись» и выведите convert(25) двумя строками: «в фаренгейтах: …» и «в кельвинах: …».

practice.py
Вопросы и ответы по уроку

Что такое абстрактный класс в Python простыми словами?

Это класс-контракт: он перечисляет методы, которые обязан реализовать каждый потомок, но сам (и любой неполный потомок) не создаёт объектов. Объявляется через базу ABC и декоратор @abstractmethod из модуля abc.

Урок 9: полиморфизм и утиная типизация

Чем ABC отличается от интерфейса в других языках?

ABC может содержать реализацию и состояние: обычные методы и атрибуты доступны потомкам через наследование, а абстрактные методы — иметь тело по умолчанию. Интерфейс в строгих языках описывает только сигнатуры. Проверка при этом мягче: сигнатуры аргументов ABC не сверяет.

Можно ли создать объект абстрактного класса?

Нет: вызов ClassName() поднимет TypeError с перечнем нереализованных методов. То же самое случится с потомком, который унаследовал абстрактные методы, но не реализовал их все.

Когда выбирать ABC, а когда утиную типизацию?

ABC — когда от класса-участника требуется гарантия: каркас фреймворка, плагины, набор стратегий. Утиная типизация — когда объекты разнородны и общего контракта нет; проверка hasattr или перехват AttributeError закрывают редкие сомнения.

Понравился урок? Сошлитесь на него

Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.

TelegramVK

Похожие уроки по темам

Подобраны автоматически по пересечению тем и ключевых слов.