ABC или Абстрактные классы в Python
Работая с сложными объектами, которые имеют похожее назначение, часто можно попасться на удочку дублирования. Решить подобную проблему, предоставить общий интерфейс классам и пришел модуль abc или Abstract Base Classes.
Кроме того, это отличный способ практиковать хорошие принципы проектирования и использования парадигм ООП.
Итак, абстрактный класс - это класс, в котором реализован хотя бы 1 абстрактный метод. Можно представить его как чертеж, по которому будут строиться дочерние классы. В свою очередь, абстрактный метод - это метод-заглушка, то есть без реализации и нужен он затем, чтобы показать, что этот метод обязательно должен быть реализован в всех дочерних классах.
Кроме того, в абстрактном классе могут присутствовать не только абстрактные методы, но и методы с конкретной реализацией, которые можно просто брать и использовать в дочерних классах.
Интересной особенностью абстрактного класса является то, что нельзя создать его экземпляр, только от дочерних классов. Кроме того, создать экземпляр дочернего класса не получится, если в нем не реализованы все абстрактные методы абстрактного базового класса.
Такие дела. А теперь пойдем смотреть, как это работает на практике.
Представим, что нам надо сделать общий интерфейс с какими-либо "методами" музыкальных инструментов. Ведь все они могут звучать, и должны быть отстроены перед игрой, только делается это по-разному. Попробуем реализовать это с помощью абстрактных классов:
from abc import ABC, abstractmethod class MusicInstruments(ABC): @abstractmethod def make_sound(self): raise NotImplementedError @abstractmethod def tune(self): raise NotImplementedError
Абстрактный класс обязательно должен быть унаследован от abc.ABC.
Я обычно явно прописываю вызов исключений в абстрактных методах вместо ...
или pass
, чтобы ошибочные попытки обратиться к реализации в абстрактном классе увенчались провалом. Выглядит это так:
class Guitar(MusicInstruments): def make_sound(self): ... def tune(self): super().tune() gibson = Guitar() gibson.tune() > NotImplementedError
А что произойдет, если мы забудем реализовать все абстрактный методы в дочернем классе?
Ну во-первых, IDE подсветит этот момент:
А во-вторых, при попытке создать экземпляр такого класса, будет вызвано иключение TypeError:
class Guitar(MusicInstruments): def make_sound(self): ... gibson = Guitar() > gibson = Guitar() > ^^^^^^^^ > TypeError: Can't instantiate abstract class Guitar with abstract method tune
А теперь рассмотрим успешный пример реализации концепции базовых классов:
from abc import ABC, abstractmethod class MusicInstruments(ABC): @abstractmethod def make_sound(self): raise NotImplementedError @abstractmethod def tune(self): raise NotImplementedError class Guitar(MusicInstruments): def make_sound(self): print("Брынь") def tune(self): print("Все струны отстроены, давай играть!") class Drums(MusicInstruments): def make_sound(self): print("Бдыщ!") def tune(self): print("Пластики подтянуты и отстроены, можно играть!") guitar = Guitar() drums = Drums() guitar.tune() drums.tune() guitar.make_sound() drums.make_sound() > Все струны отстроены, давай играть! > Пластики подтянуты и отстроены, можно играть! > Брынь > Бдыщ!
Ну вот, другое дело! Теперь наши классы построены по образу и подобию абстрактного класса MusicInstruments и мы стали на несколько шагов ближе к написанию качественного кода.