# 29.7. `abc` — Classes Base Abstratas

Código Fonte: Lib/abc.py

Este módulo fornece a infraestrutura para definir abstract base classes (CBAs) em Python, como delineado em PEP 3119; veja o PEP para entender o porquê isto foi adicionado ao Python. (Veja também PEP 3141 e o módulo `numbers` sobre uma hierarquia de tipos para números baseado nas CBAs.)

O módulo `collections` tem algumas classes concretas que derivam de CBAs; essas podem, evidentemente, ser ainda mais derivadas. Além disso, o submódulo `collections.abc` tem algumas CBAs que podem ser usadas para testar se uma classe ou instância oferece uma interface particular, por exemplo, se é hashable ou se é um mapping.

Este módulo fornece a metaclasse `ABCMeta` para definir CBAs e uma classe de ajuda `ABC` para alternativamente definir CBAs através de herança:

class `abc.``ABC`

  Uma classe auxiliar que tem :classe:`ABCMeta` como sua metaclasse. Com essa classe, uma classe base abstrata pode ser criada simplesmente derivando da :classe:`ABC` evitando às vezes confundir o uso da metaclasse, por exemplo:

      from abc import ABC

      class MyABC(ABC):
          pass

  Note que o tipo de classe `ABC` ainda é :classe:`ABCMeta`, portanto herdando da :classe:`ABC` requer as precauções usuais a respeito do uso da metaclasse, como herança múltipla pode levar a conflitos de metaclasse. Pode-se também definir uma classe base abstrata ao passar a palavra reservada da metaclasse e usar :classe:`ABCMeta` diretamente, por exemplo:

      from abc import ABCMeta

      class MyABC(metaclass=ABCMeta):
          pass

  Novo na versão 3.4.

class `abc.``ABCMeta`

  Metaclasse para definir Classe Base Abstrata (CBAs).

  Use esta metaclasse para criar uma CBA. Uma CBA pode ser diretamente subclasseada, e então agir como uma classe misturada. Você também pode registrar classes concretas não relacionadas (até mesmo classes embutidas) e CBAs não relacionadas como “subclasses virtuais” – estas e suas descendentes serão consideradas subclasses da CBA de registro pela função embutida `issubclass()`, mas a CBA de registro não irá aparecer na ORM (Ordem de Resolução do Método) e nem as implementações do método definidas pela CBA de registro será chamável (nem mesmo via `super()`). 1

  Classes criadas com a metaclasse de :classe:`ABCMeta` tem o seguinte método:

  `register`(subclass)

    Registrar subclasse como uma “subclasse virtual” desta CBA. Por exemplo:

        from abc import ABC

        class MyABC(ABC):
            pass

        MyABC.register(tuple)

        assert issubclass(tuple, MyABC)
        assert isinstance((), MyABC)

    Alterado na versão 3.3: Retorna a subclasse registrada, para permitir o uso como um decorador de classe.

    Alterado na versão 3.4: Para detectar chamadas para `register()`, você pode usar a função `get_cache_token()`.

  Você também pode sobrepor este método em uma classe base abstrata:

  `__subclasshook__`(subclass)

    (Deve obrigatoriamente ser definido como um método de classe.)

    Cheque se a subclasse é considerada uma subclasse desta CBA. Isto significa que você pode customizar ainda mais o comportamento da `issubclass` sem a necessidade de chamar `register()` em toda classe que você queira considerar uma subclasse da CBA. (Este método de classe é chamado do método da CBA `__subclasscheck__()` .)

    Este método deve retornar `True`, `False` ou `NotImplemented`. Se retornar `True`, a subclasse é considerada uma subclasse desta CBA. Se retornar `False`, a subclasse não é considerada uma subclasse desta CBA, mesmo que normalmente seria uma. Se retornar `NotImplemented`, a verificação da subclasse é continuada com o mecanismo usual.

  Para uma demonstração destes conceitos, veja este exemplo de definição CBA:

      class Foo:
          def __getitem__(self, index):
              ...
          def __len__(self):
              ...
          def get_iterator(self):
              return iter(self)

      class MyIterable(ABC):

          @abstractmethod
          def __iter__(self):
              while False:
                  yield None

          def get_iterator(self):
              return self.__iter__()

          @classmethod
          def __subclasshook__(cls, C):
              if cls is MyIterable:
                  if any("__iter__" in B.__dict__ for B in C.__mro__):
                      return True
              return NotImplemented

      MyIterable.register(Foo)

  A CBA `Mylterable` define o método iterável padrão, `__iter__()`, como um método abstrato. A implementação dada aqui pode ainda ser chamada da subclasse. O método `get_iterator()` é também parte da classe base abstrata `MyIterable`, mas não precisa ser substituído nas classes derivadas não abstratas.

  O método de classe `__subclasshook__()` definido aqui diz que qualquer classe que tenha um método `__iter__()` em seu `__dict__` (ou no de uma de suas classes base, acessados via lista `__mro__`) é considerado uma `MyIterable` também.

  Finalmente, a última linha faz de `Foo` uma subclasse virtual da `MyIterable`, apesar de não definir um método `__iter__()` (ela usa o protocolo iterável antigo, definido em termos de `__len__()` e `__getitem__()`). Note que isto não fará o `get_iterator` disponível como um método de `Foo`, então ele é fornecido separadamente.

O módulo `abc` também fornece o seguinte decorador:

`@``abc.``abstractmethod`

  Um decorator indicando métodos abstratos.

  Usar este decorador requer que a metaclasse da classe seja :classe:`ABCMeta` ou seja derivada desta. Uma classe que tem uma metaclasse derivada de :classe:`ABCMeta` não pode ser instanciada a menos que todos os seus métodos abstratos e propriedades estejam substituídos. Os métodos abstratos podem ser chamados usando qualquer um dos mechanismos normais de ‘super’ chamadas. `abstractmethod()` pode ser usado para declarar métodos abstratos para propriedades e descritores.

  Adicionar dinamicamente métodos abstratos a uma classe, ou tentar modificar o status de abstração de um método ou classe uma vez que estejam criados, não é tolerado. A `abstractmethod()` afeta apenas subclasses derivadas usando herança regular; “subclasses virtuais” registradas com o método da CBA `register()` não são afetadas.

  Quando `abstractmethod()` é aplicado em combinação com outros descriptores de método, ele deve ser aplicado como o decorador mais interno, como mostrado nos seguintes exemplos de uso:

      class C(ABC):
          @abstractmethod
          def my_abstract_method(self, ...):
              ...
          @classmethod
          @abstractmethod
          def my_abstract_classmethod(cls, ...):
              ...
          @staticmethod
          @abstractmethod
          def my_abstract_staticmethod(...):
              ...

          @property
          @abstractmethod
          def my_abstract_property(self):
              ...
          @my_abstract_property.setter
          @abstractmethod
          def my_abstract_property(self, val):
              ...

          @abstractmethod
          def _get_x(self):
              ...
          @abstractmethod
          def _set_x(self, val):
              ...
          x = property(_get_x, _set_x)

  Para que interopere corretamente com o maquinário da classe base abstrata, o descritor precisa identificar-se como abstrato usando `__isabstractmethod__`. No geral, este atributo deve ser `True` se algum dos métodos usados para compor o descritor for abstrato. Por exemplo, a :classe:`property` embutida do Python faz o equivalente a:

      class Descriptor:
          ...
          @property
          def __isabstractmethod__(self):
              return any(getattr(f, '__isabstractmethod__', False) for
                         f in (self._fget, self._fset, self._fdel))

  Nota

  Diferente de métodos abstratos Java, esses métodos abstratos podem ter uma implementação. Esta implementação pode ser chamada via mecanismo da `super()` da classe que a substitui. Isto pode ser útil como um ponto final para uma super chamada em um framework que usa herança múltipla cooperativa.

O módulo `abc` também suporta os seguintes decoradores herdados:

`@``abc.``abstractclassmethod`

  Novo na versão 3.2.

  Obsoleto desde a versão 3.3: Agora é possível usar :classe:`classmethod` com `abstractmethod()`, tornando redundante este decorador.

  Uma subclasse da `classmethod()` embutida, indicando um método de classe abstrato. Caso contrário, é similar à `abstractmethod()`.

  Este caso especial está depreciado, pois o decorador da `classmethod()` está agora corretamente identificado como abstrato quando aplicado a um método abstrato:

      class C(ABC):
          @classmethod
          @abstractmethod
          def my_abstract_classmethod(cls, ...):
              ...

`@``abc.``abstractstaticmethod`

  Novo na versão 3.2.

  Obsoleto desde a versão 3.3: Agora é possível usar :classe:`staticmethod` com `abstractmethod()`, tornando redundante este decorador.

  Uma subclasse da `staticmethod()` embutida, indicando um método estático abstrato. Caso contrário, ela é similar à `abstractmethod()`.

  Este caso especial está descontinuado, pois o decorador da `staticmethod()` está agora corretamente identificado como abstrato quando aplicado a um método abstrato:

      class C(ABC):
          @staticmethod
          @abstractmethod
          def my_abstract_staticmethod(...):
              ...

`@``abc.``abstractproperty`

  Obsoleto desde a versão 3.3: Agora é possível usar :classe:`property`, `property.getter()`, `property.setter()` e `property.deleter()` com `abstractmethod()`, tornando redundante este decorador.

  Uma subclasse da `property()` embutida, indicando uma propriedade abstrata.

  Este caso especial está descontinuado, pois o decorador da `property()` está agora corretamente identificado como abstrato quando aplicado a um método abstrato:

      class C(ABC):
          @property
          @abstractmethod
          def my_abstract_property(self):
              ...

  O exemplo acima define uma propriedade somente leitura; você também pode definir uma propriedade abstrata de leitura e gravação marcando apropriadamente um ou mais dos métodos subjacentes como abstratos:

      class C(ABC):
          @property
          def x(self):
              ...

          @x.setter
          @abstractmethod
          def x(self, val):
              ...

  Se apenas alguns componentes são abstratos, apenas estes componentes precisam ser atualizados para criar uma propriedade concreta em uma subclasse:

      class D(C):
          @C.x.setter
          def x(self, val):
              ...

O módulo: mod: abc também fornece as seguintes funções:

`abc.``get_cache_token`()

  Retorna o token de cache da classe base abstrata atual.

  O token é um objeto opaco (que suporta teste de igualdade) identificando a versão atual do cache da classe base abstrata para subclasses virtuais. O token muda a cada chamada ao `ABCMeta.register()` em qualquer CBA.

  Novo na versão 3.4.

Notas de rodapé

1

  Programadores C++ devem notar que o conceito da casse base virtual do Python não é o mesmo que o de C++.