Quando você cria e instancia uma classe em Python, muita coisa acontece e acho que nem todos estão familiarizados com os passos.
Vamos dar uma olhada na sequência.
O construtor recebe como parâmetros a própria metaclasse, o nome da classe (
Caso você sobrescreva o construtor da metaclasse, ele precisa retornar o objeto instanciado, que você obtém do construtor da classe pai da metaclasse.
Em seguida o método de inicialização (
É recomendável não sobrescrever o construtor da metaclasse. Qualquer procedimento pode ser tranquilamente executado nos métodos e inicialização e de chamada.
Em seguida é evocado o construtor da classe, recebendo a classe e quaisquer parâmetros passados no comando de instanciação.
É obrigatório que a instância criada pelo construtor de
Após o construtor, o método de inicialização da classe é chamado, recebendo como parâmetros o retorno do construtor e quaisquer parâmetros passados no comando de instanciação.
Vamos dar uma olhada na sequência.
Criação da classe
Quando você cria uma classe com o statementclass
, a primeira coisa que acontece é o construtor (__new__
) da metaclasse (por padrão type
) ser chamado.O construtor recebe como parâmetros a própria metaclasse, o nome da classe (
str
), uma tupla contendo as classes base da classe criada e um dicionário com métodos e atributos declarados no corpo da classe.Caso você sobrescreva o construtor da metaclasse, ele precisa retornar o objeto instanciado, que você obtém do construtor da classe pai da metaclasse.
Em seguida o método de inicialização (
__init__
) da metaclasse é chamado, recebendo como parâmetros o retorno do construtor, o nome da classe, a tupla de classes base e o dicionário de métodos e atributos.É recomendável não sobrescrever o construtor da metaclasse. Qualquer procedimento pode ser tranquilamente executado nos métodos e inicialização e de chamada.
Instanciação da classe
Quando você instancia a classe, o primeiro método a ser chamado é o método de chamada (__call__
) da metaclasse. Ele recebe como parâmetros a classe e quaisquer parâmetros passados no comando de instanciação.Em seguida é evocado o construtor da classe, recebendo a classe e quaisquer parâmetros passados no comando de instanciação.
É obrigatório que a instância criada pelo construtor de
super
seja retornada, caso o construtor seja sobrescrito.Após o construtor, o método de inicialização da classe é chamado, recebendo como parâmetros o retorno do construtor e quaisquer parâmetros passados no comando de instanciação.
Chamando a instância
Caso o método de chamada seja implementado na classe, a instância é “chamável” (callable). Na chamada o método de chamada é executado recebendo a instância e quaisquer parâmetros passados na chamada da instância.Quem é quem
Um pequeno código apenas com boilerplates que não executam nada, com o único objetivo de demonstrar onde fica cada método citado:class Metaclasse(type):
def __new__(meta, name, base, dict_):
""" Este é o construtor da metaclasse """
return type.__new__(meta, name, base, dict_)
def __init__(cls, name, base, dict_):
""" Este é o método de inicialização da metaclasse """
type.__init__(cls, name, base, dict_)
def __call__(cls, *args, **kwargs):
""" Este é o método de chamada da metaclasse """
return type.__call__(cls, *args, **kwargs)
class Classe(object):
__metaclass__ = Metaclasse
def __new__(cls, *args, **kwargs):
""" Este é o construtor da classe """
return super(Classe, cls).__new__(cls)
def __init__(self, *args, **kwargs):
"""
Este é o método de inicialização da classe.
Como ela herda de object, não há necessidade de
chamar super, mas quando houver, a forma é:
super(Classe, self).__init__(*args, **kwargs)
"""
def __call__(self, *args, **kwargs):
""" Este é o método de chamada da classe """
return None
[update 2015-04-18] Esta é a versão do código acima em Python 3:[/update]class Metaclasse(type): def __new__(meta, name: str, base: tuple, dict_: dict) -> type: """ Este é o construtor da metaclasse """ return type.__new__(meta, name, base, dict_) def __init__(cls, name: str, base: tuple, dict_: dict): """ Este é o método de inicialização da metaclasse """ type.__init__(cls, name, base, dict_) def __call__(cls, *args, **kwargs) -> object: """ Este é o método de chamada da metaclasse """ return type.__call__(cls, *args, **kwargs) class Classe(object, metaclass=Metaclasse): def __new__(cls, *args, **kwargs) -> object: """ Este é o construtor da classe """ return super().__new__(cls) def __init__(self, *args, **kwargs): """ Este é o método de inicialização da classe. Como ela herda de object, não há necessidade de chamar super, mas quando houver, a forma é: super().__init__(*args, **kwargs) """ def __call__(self, *args, **kwargs) -> None: """ Este é o método de chamada da classe """ return None
Dois dedos de prosa sobre método destruidor
O método destruidor (__del__
) não é chamado quando o objeto perde todas as referências ativas, mas sim quando é coletado pelo garbage collector (gc
).Como o
gc
não tem hora certa para rodar e seu comportamento varia muito de uma implementação de Python para outra, não é recomendável confiar nele para executar procedimentos críticos.O que você pode fazer é usá-lo para garantir determinados estados que podem ter sido esquecidos ou perdidos depois que a instância ficou sem referências.
Observações finais
As assinaturas do construtor e do método de inicialização da classe devem ser rigorosamente iguais.[update]
Um detalhe importante é que, se o método de inicialização for sobrescrito alterando sua assinatura, não há necessidade de sobrescrever o construtor, porém se o construtor for sobrescrito alterando sua assinatura, é obrigatório que o método de inicialização também seja sobrescrito.
Detalhes da linguagem…
[/update]
A chamada do método de chamada da classe pai da meta classe deve passar os parâmetros esperados pelos argumentos nas assinaturas do construtor e da inicialização da classe. No exemplo acima, esta chamada (precedida por
return
) é:
type.__call__(cls, *args, **kwargs)
[update]
Um detalhe que esqueci de mencionar é que é justamente nessa chamada que o construtor e o método de inicialização são evocados.
[/update]
Os parâmetros passados são os esperados nas assinaturas citadas.
[]’s
Cacilhας, La Batalema