Criando metaclasses personalizadas
Em Python, dá para personalizar o processo de criação de classes passando a palavra-chave metaclass na definição da classe. Isso também pode ser feito herdando de uma classe que já define essa palavra‑chave.
class MyMeta(type):
pass
class MyClass(metaclass=MyMeta):
pass
class MySubclass(MyClass):
pass
Veja abaixo que o tipo da classe MyMeta é type e que o tipo de MyClass e MySubClass é MyMeta.
print(type(MyMeta))
print(type(MyClass))
print(type(MySubclass))
<class 'type'>
<class '__main__.MyMeta'>
<class '__main__.MyMeta'>
Quando nenhuma metaclass é definida, a metaclass padrão type é usada. Se uma metaclass for informada e não for uma instância de type(), então ela é usada diretamente como metaclass.
__new__ e __init__
Metaclasses também podem ser definidas de duas formas, como mostrado abaixo. Explicamos a diferença na sequência.
class MetaOne(type):
def __new__(cls, name, bases, dict):
pass
class MetaTwo(type):
def __init__(self, name, bases, dict):
pass
__new__ é usado quando você quer definir o dicionário (dict) ou a tupla de bases antes da criação da classe. O valor de retorno de __new__ geralmente é uma instância de cls. __new__ permite que subclasses de tipos imutáveis personalizem a criação de instâncias, e pode ser sobrescrito em metaclasses personalizadas para customizar a criação de classes. Já __init__ é chamado depois que o objeto foi criado, para inicializá‑lo.
Método __call__ da metaclass
De acordo com a documentação oficial, também é possível sobrescrever outros métodos de classe definindo um método __call__() personalizado na metaclass, permitindo um comportamento específico quando a classe é chamada.
Método __prepare__ da metaclass
Segundo o data model do Python
Após identificar a metaclass apropriada, o namespace da classe é preparado. Se a metaclass tiver o atributo __prepare__, ele é chamado como namespace = metaclass.__prepare__(name, bases, **kwds) (os argumentos nomeados adicionais, se houver, vêm da definição da classe). Se a metaclass não tiver __prepare__, o namespace da classe é inicializado como um mapeamento ordenado vazio. - docs.python.org
Padrão Singleton usando uma metaclass
Esse é um padrão de projeto que restringe a instanciação de uma classe a um único objeto. Pode ser útil, por exemplo, ao projetar uma classe de conexão com banco de dados, quando você quer manter apenas uma instância da conexão.
class SingletonMeta(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(SingletonMeta,cls).__call__(*args, **kwargs)
return cls._instances[cls]
class SingletonClass(metaclass=SingletonMeta):
pass
Conclusão
Neste artigo, você viu o que são metaclasses e como implementá‑las na sua programação em Python. Metaclasses podem ser aplicadas em logging, registro de classes no momento da criação, profiling, entre outros. Elas parecem conceitos bem abstratos — e talvez você esteja se perguntando se realmente precisa usá‑las. Tim Peters, veterano do Python, responde isso da melhor forma.
"Metaclasses são uma magia mais profunda do que 99% dos usuários deveria se preocupar. Se você está se perguntando se precisa delas, não precisa (as pessoas que realmente precisam sabem com certeza que precisam, e não precisam de explicações sobre o porquê)." - realpython
Quer aprender mais sobre Python? Comece a trilha de habilidades Python Programming da DataCamp e confira nosso tutorial de classes em Python.
Uma metaclass em Python é a classe das classes — ela define como uma classe se comporta. Uma classe, por sua vez, é uma instância de uma metaclass. Em Python, uma classe define como as instâncias dessa classe vão se comportar. Para entender bem metaclasses, é importante já ter experiência trabalhando com classes em Python. Antes de mergulhar no tema, vamos tirar alguns conceitos do caminho.
Tudo em Python é um objeto
Classes em Python podem ser criadas dinamicamente
typeem Python permite descobrir o tipo de um objeto. Vamos verificar o tipo do objeto que criamos acima.Espera aí, o que foi isso? A gente esperaria que o tipo do objeto criado acima fosse class, mas não é. Guarda essa ideia — já já a gente volta nela. Também percebemos que o tipo de
typeétype. Ou seja, ele é uma instância detype. Outra coisa interessante quetypefaz é permitir criar classes dinamicamente. Veja como fazer isso abaixo. A classeDataCampmostrada abaixo poderia ser criada usandotypeassim:No exemplo acima,
DataCampé o nome da classe, enquantoDataCampClassé a variável que guarda a referência da classe. Ao usartype, podemos passar atributos da classe via um dicionário, como abaixo:Se quisermos que a nossa
PythonClassherde da classeDataCamp, passamos essa classe como segundo argumento ao definir comtype:Com esses dois conceitos em mente, fica claro que o Python cria classes usando uma metaclass. Vimos que tudo em Python é um objeto — e esses objetos são criados por metaclasses. Sempre que usamos
classpara criar uma classe, existe uma metaclass fazendo a mágica por trás dos panos. Já vimos otypefazendo isso na prática. É parecido comstr, que cria strings, eint, que cria inteiros. Em Python, o atributo___class__permite verificar o tipo da instância atual. Vamos criar uma string e checar seu tipo:Também podemos checar o tipo usando
type(article).Ao verificar o tipo de
str, descobrimos que ele étype.O mesmo acontece ao verificar
float,int,list,tupleedict. Todos esses objetos são do tipotype.Já vimos que
typecria classes. Portanto, quando verificamos o__class__de__class__, o retorno deve sertype.