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++.
