Абстрактные классы: 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("старт"))
1. Что произойдёт при попытке создать экземпляр абстрактного класса?
2. Как объявить обязательный метод в ABC?
3. Когда Python проверяет, что потомок реализовал все абстрактные методы?
4. Может ли абстрактный метод иметь реализацию?
5. Зачем ABC, если duck typing и так работает?
Создайте абстрактный класс Converter с абстрактным методом convert(celsius). Реализуйте потомков ToFahrenheit (формула c * 1.8 + 32) и ToKelvin (c + 273.15). Пройдитесь циклом по паре «конвертер — подпись» и выведите convert(25) двумя строками: «в фаренгейтах: …» и «в кельвинах: …».
Что такое абстрактный класс в Python простыми словами?
Это класс-контракт: он перечисляет методы, которые обязан реализовать каждый потомок, но сам (и любой неполный потомок) не создаёт объектов. Объявляется через базу ABC и декоратор @abstractmethod из модуля abc.
Урок 9: полиморфизм и утиная типизацияЧем ABC отличается от интерфейса в других языках?
ABC может содержать реализацию и состояние: обычные методы и атрибуты доступны потомкам через наследование, а абстрактные методы — иметь тело по умолчанию. Интерфейс в строгих языках описывает только сигнатуры. Проверка при этом мягче: сигнатуры аргументов ABC не сверяет.
Можно ли создать объект абстрактного класса?
Нет: вызов ClassName() поднимет TypeError с перечнем нереализованных методов. То же самое случится с потомком, который унаследовал абстрактные методы, но не реализовал их все.
Когда выбирать ABC, а когда утиную типизацию?
ABC — когда от класса-участника требуется гарантия: каркас фреймворка, плагины, набор стратегий. Утиная типизация — когда объекты разнородны и общего контракта нет; проверка hasattr или перехват AttributeError закрывают редкие сомнения.
Понравился урок? Сошлитесь на него
Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.
Что читать дальше
ООП · Урок 9
Полиморфизм и утиная типизация в Python
Полиморфизм — когда разные классы отвечают на один и тот же вызов каждый по-своему, а ваш код не интересуется, чей метод выполнится. Разбираем один интерфейс, утиную типизацию и функции над разнородными объектами.
ООП · Урок 11
Магические методы: __str__ и __repr__
print(obj) без подготовки показывает тип и адрес в памяти. Магические методы __str__ и __repr__ позволяют самим решить, как объект превращается в строку, — человек увидит «21°C», а отладчик — Temperature(21).
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
pytest · Урок 16
Тестирование классов: методы, состояние и Test-классы
Корзина магазина как подопытная: фикстура раздаёт каждому тесту свежий объект, Test-класс наводит порядок в методах — и ни одно состояние не протекает.
pytest Test класстестирование методов класса python
FastAPI · Урок 5
CRUD на FastAPI: POST, PUT, PATCH и DELETE методы
Строим полный CRUD на FastAPI: POST со статусом 201, PUT с защитой от 404, PATCH и DELETE — и собираем мини-сервис задач целиком.
fastapi put методfastapi delete метод
ООП · Урок 1
Классы в Python: первый объект
Класс — чертёж, объект — экземпляр по этому чертежу. Создаём первый класс, вызываем методы и убеждаемся: в Python объектом является всё, включая функции.
классы pythonpython классы