Абстрактные базовые классы (ABC)

Введение

Примеры

  • 2

    Установка метакласса ABCMeta

    Абстрактные классы - это классы, которые предназначены для наследования, но избегают реализации конкретных методов, оставляя только сигнатуры методов, которые должны реализовывать подклассы.

    Абстрактные классы полезны для определения и реализации абстракций классов на высоком уровне, аналогично концепции интерфейсов в типизированных языках, без необходимости реализации метода.

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

     class Fruit:
    
        def check_ripeness(self):
            raise NotImplementedError("check_ripeness method not implemented!")
    
    
    class Apple(Fruit):
        pass
    
    
    a = Apple()
    a.check_ripeness() # raises NotImplementedError
    
    
     

    Создание абстрактного класса таким способом предотвращает ненадлежащее использование методов, которые не переопределяются, и, конечно, поощряет определение методов в дочерних классах, но не обеспечивает их определение. С abc модуля мы можем предотвратить дочерние классы от того экземпляра , когда они не могут переопределить абстрактные методы класса своих родителей и предков:

     from abc import ABCMeta
    
    class AbstractClass(object):
        # the metaclass attribute must always be set as a class variable 
        __metaclass__ = ABCMeta
    
       # the abstractmethod decorator registers this method as undefined
       @abstractmethod 
       def virtual_method_subclasses_must_define(self):
           # Can be left completely blank, or a base implementation can be provided
           # Note that ordinarily a blank interpretation implicitly returns `None`, 
           # but by registering, this behaviour is no longer enforced.
    
     

    Теперь можно просто создать подкласс и переопределить:

     class Subclass(AbstractClass):
        def virtual_method_subclasses_must_define(self):
            return 
  • 1

    Почему / Как использовать ABCMeta и @abstractmethod

    Абстрактные базовые классы (ABC) определяют, какие производные классы реализуют конкретные методы из базового класса.

    Чтобы понять, как это работает и почему мы должны его использовать, давайте рассмотрим пример, который понравился бы Ван Россуму. Допустим, у нас есть базовый класс «MontyPython» с двумя методами (joke & punchline), который должен быть реализован всеми производными классами.

     class MontyPython:
        def joke(self):
            raise NotImplementedError()
    
        def punchline(self):
            raise NotImplementedError()
    
    class ArgumentClinic(MontyPython):
        def joke(self):
            return "Hahahahahah"
    
     

    Когда мы создаем объект и называем это два метода, мы получим ошибку (как и ожидалось) с punchline() метод.

      >>> sketch = ArgumentClinic() 
     >>> sketch.punchline() 
    NotImplementedError 
    
     

    Однако это все еще позволяет нам создавать экземпляр объекта класса ArgumentClinic без получения ошибки. На самом деле мы не получим ошибку, пока не найдем punchline ().

    Этого можно избежать, используя модуль Abstract Base Class (ABC). Давайте посмотрим, как это работает на том же примере:

     from abc import ABCMeta, abstractmethod
    
    class MontyPython(metaclass=ABCMeta):
        @abstractmethod
        def joke(self):
            pass
    
    @abstractmethod
    def punchline(self):
        pass
    
    class ArgumentClinic(MontyPython):
        def joke(self):
            return "Hahahahahah"
    
     

    На этот раз, когда мы пытаемся создать экземпляр объекта из неполного класса, мы немедленно получаем TypeError!

     >>> c = ArgumentClinic()
    TypeError:
    "Can't instantiate abstract class ArgumentClinic with abstract methods punchline"
    
     

    В этом случае легко завершить класс, чтобы избежать любых ошибок типа:

     class ArgumentClinic(MontyPython):
        def joke(self):
            return "Hahahahahah"
    
        def punchline(self):
            return "Send in the constable!"
    
     

    На этот раз, когда вы создаете экземпляр объекта, он работает!

Синтаксис

Параметры

Примечания