Python. Абстрактные базовые классы
Перевод параграфа 6.7 Abstract Base Classes из книги Intermediate Pythonopen in new window.
Чтобы классы реализовывали заданный набор методов в статически типизированных языках, таких как Java, используются интерфейсы и абстрактные классы.
Простая реализация такого контракта в Python — добавить в базовый класс методы по умолчанию, выбрасывающие исключение NotImplementedError
. Такое решение неполное: наследники могут не переопределить все методы базового класса, а проблема обнаружится только во время выполнения программы.
Рассмотрим другую ситуацию — использование одного объекта для замещения другого. Заместительopen in new window перехватывает все вызовы и передаёт их в скрываемый объект. Заместитель реализует все нужные методы, но проверку типа через isinstance
он не проходит, так как имеет тип отличный от замещаемого объекта.
В Python такие задачи решаются через абстрактные базовые классы, реализуемые модулем abc
. Этот модуль определяет мета-класс и набор декораторов. Для определения абстрактного базового класса мы устанавливаем ABCMeta
как мета-класс абстрактного класса и помечаем декораторами @abstractmethod
и @abstractproperty
методы и свойства которые должны быть реализованы в неабстрактных потомках.
Если потомки не реализуют абстрактные методы и свойства то не смогут создавать объекты:
from abc import ABCMeta, abstractmethod
class Vehicle(object):
__meta-class__ = ABCMeta
@abstractmethod
def change_gear(self):
pass
@abstractmethod
def start_engine(self):
pass
class Car(Vehicle):
def __init__(self, make, model, color):
self.make = make
self.model = model
self.color = color
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# abstract methods not implemented
>>> car = Car("Toyota", "Avensis", "silver")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Car with abstract methods change_gear, start_engine
2
3
4
5
6
Как только класс реализовал все абстрактные методы появляется возможность создавать объекты:
from abc import ABCMeta, abstractmethod
class Vehicle(object):
__meta-class__ = ABCMeta
@abstractmethod
def change_gear(self):
pass
@abstractmethod
def start_engine(self):
pass
class Car(Vehicle):
def __init__(self, make, model, color):
self.make = make
self.model = model
self.color = color
def change_gear(self):
print("Changing gear")
def start_engine(self):
print("Changing engine")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
>>> car = Car("Toyota", "Avensis", "silver")
>>> print(isinstance(car, Vehicle))
True
2
3
Абстрактные классы позволяет регистрировать существующие классы как часть своей иерархии, не проводя проверок на реализацию методов и свойств. Это простое решение второй проблемы открывающей параграф. Абстрактный класс регистрирует класс заместитель и проверка isinstance
возвращает корректное значение:
from abc import ABCMeta, abstractmethod
class Vehicle(object):
__meta-class__ = ABCMeta
@abstractmethod
def change_gear(self):
pass
@abstractmethod
def start_engine(self):
pass
class Car(object):
def __init__(self, make, model, color):
self.make = make
self.model = model
self.color = color
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>>> Vehicle.register(Car)
>>> car = Car("Toyota", "Avensis", "silver")
>>> print(isinstance(car, Vehicle))
True
2
3
4
Абстрактные базовые классы широко используются в библиотеке Python. Они предоставляют средство для группировки классов, например, числовых типов, которые имеют относительно плоскую иерархию. Модуль collections
также содержит абстрактные базовые классы для различных наборов операций с множествами, последовательностями и словарями. Абстрактные базовые классы Python предоставляют возможность применять контракты между классами такие же как интерфейсы в Java.