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