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 viasuper()
). 1Classes 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çãoget_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 chamarregister()
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
ouNotImplemented
. Se retornarTrue
, a subclasse é considerada uma subclasse desta CBA. Se retornarFalse
, a subclasse não é considerada uma subclasse desta CBA, mesmo que normalmente seria uma. Se retornarNotImplemented
, 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étodoget_iterator()
é também parte da classe base abstrataMyIterable
, 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 umaMyIterable
também.Finalmente, a última linha faz de
Foo
uma subclasse virtual daMyIterable
, 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á oget_iterator
disponível como um método deFoo
, 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 CBAregister()
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 serTrue
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()
eproperty.deleter()
comabstractmethod()
, 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++.