Metaclasse é a classe de uma classe, assim como classe é de objeto.
Ela define como as classes se comportam e são construídas.
Primeiramente, type é a metaclasse padrão do Python.
Por exemplo, type(MinhaClasse) retorna <class 'type'>.
Além disso, você pode criar metaclasses personalizadas.
A voz passiva é usada aqui: “as classes são criadas dinamicamente pela metaclasse”.
Quando utilizar metaclasses? Em frameworks e bibliotecas complexas.
Também para validação de classes ou registro automático.
Metaclasses são um recurso avançado e poderoso.
Vamos explorar type, __new__ e __init__ da metaclasse.
Três subtítulos guiarão você por esse tópico profundo.
Ao final, você entenderá o coração da orientação a objetos.
Type: a metaclasse padrão
type é a metaclasse de todas as classes em Python.
Você pode criar classes dinamicamente com type(nome, bases, dicionário).
Quando usar criação dinâmica? Em código gerado automaticamente.
Também em metaprogramação avançada.
A voz passiva é aplicada: “os atributos são passados como dicionário”.
Exemplo de type como metaclasse:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# Forma tradicional (usando class) class AnimalTradicional: def __init__(self, nome): self.nome = nome def falar(self): return "Som genérico" # Mesma coisa, criada dinamicamente com type def falar_dinamico(self): return "Som genérico" AnimalDinamico = type('AnimalDinamico', (object,), { '__init__': lambda self, nome: setattr(self, 'nome', nome), 'falar': falar_dinamico }) # Demonstração print("=== Criando Classes com type ===\n") tradicional = AnimalTradicional("Rex") dinamico = AnimalDinamico("Bob") print(f"Tradicional: {tradicional.nome} - {tradicional.falar()}") print(f"Dinâmico: {dinamico.nome} - {dinamico.falar()}") print(f"type(AnimalTradicional): {type(AnimalTradicional)}") print(f"type(AnimalDinamico): {type(AnimalDinamico)}") # Criando classe com métodos e propriedades def __init__(self, x, y): self.x = x self.y = y def __add__(self, outro): return Ponto(self.x + outro.x, self.y + outro.y) def __repr__(self): return f"Ponto({self.x}, {self.y})" Ponto = type('Ponto', (object,), { '__init__': __init__, '__add__': __add__, '__repr__': __repr__ }) p1 = Ponto(3, 4) p2 = Ponto(1, 2) print(f"\nPonto: {p1}") print(f"p1 + p2 = {p1 + p2}") # Verificando a cadeia de metaclasses print(f"\nCadeia de tipos:") print(f"type(p1): {type(p1)}") print(f"type(Ponto): {type(Ponto)}") print(f"type(type(Ponto)): {type(type(Ponto))}") |
type é a base de todo sistema de classes.
Toda classe em Python é uma instância de type.
Criando metaclasses personalizadas
Uma metaclasse personalizada herda de type.
Sobrescreva __new__ para interceptar a criação da classe.
Use __init__ para modificar a classe após criada.
Quando usar metaclasse personalizada? Em ORMs e frameworks web.
Também para singletons ou validação de interfaces.
A voz passiva é aplicada: “os atributos da classe são inspecionados”.
Exemplo de metaclasse personalizada:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
class SingletonMeta(type): """Metaclasse que implementa o padrão Singleton.""" _instancias = {} def __call__(cls, *args, **kwargs): """Controla a criação de instâncias.""" if cls not in cls._instancias: print(f"Criando primeira instância de {cls.__name__}") cls._instancias[cls] = super().__call__(*args, **kwargs) else: print(f"Retornando instância existente de {cls.__name__}") return cls._instancias[cls] class Configuracao(metaclass=SingletonMeta): """Classe Singleton para configurações.""" def __init__(self, ambiente="producao"): self.ambiente = ambiente print(f"Configuração inicializada para {ambiente}") # Demonstração print("=== Singleton com Metaclasse ===\n") c1 = Configuracao("desenvolvimento") c2 = Configuracao("producao") print(f"c1 é c2? {c1 is c2}") print(f"c1.ambiente: {c1.ambiente}") print(f"c2.ambiente: {c2.ambiente}") # Metaclasse para validação de classes class ValidadorMeta(type): """Metaclasse que valida se métodos obrigatórios existem.""" def __new__(mcs, nome, bases, dicionario): """Valida a classe antes de criá-la.""" if 'processar' not in dicionario: raise TypeError(f"Classe {nome} deve implementar método 'processar'") if 'nome' not in dicionario: raise TypeError(f"Classe {nome} deve ter atributo 'nome'") print(f"Classe {nome} validada com sucesso!") return super().__new__(mcs, nome, bases, dicionario) class ProcessadorValido(metaclass=ValidadorMeta): nome = "Processador Válido" def processar(self, dados): return f"Processando: {dados}" # Esta classe falharia (descomente para testar) # class ProcessadorInvalido(metaclass=ValidadorMeta): # nome = "Inválido" # # Faltou o método processar! print("\n=== Validação de Classes ===") p = ProcessadorValido() print(p.processar("dados importantes")) # Metaclasse para registro automático class RegistradorMeta(type): """Metaclasse que registra todas as classes criadas.""" _classes = [] def __new__(mcs, nome, bases, dicionario): nova_classe = super().__new__(mcs, nome, bases, dicionario) mcs._classes.append(nova_classe) print(f"Registrada classe: {nome}") return nova_classe @classmethod def listar_classes(mcs): return mcs._classes.copy() class PluginA(metaclass=RegistradorMeta): pass class PluginB(metaclass=RegistradorMeta): pass class PluginC(metaclass=RegistradorMeta): pass print("\n=== Registro Automático ===") for cls in RegistradorMeta.listar_classes(): print(f"Classe registrada: {cls.__name__}") |
Metaclasses interceptam a criação de classes no nível mais alto. Elas são extremamente poderosas, mas use com moderação.
__new__ e __init__ na metaclasse
__new__ cria a classe antes dela existir.
__init__ inicializa a classe já criada.
__call__ controla a criação de instâncias.
Quando usar cada um? __new__ para modificar a estrutura.
__init__ para adicionar atributos ou métodos extras.
__call__ para singletons ou pools de objetos.
A voz passiva é aplicada: “os atributos são adicionados após a criação”.
Exemplo completo com todos os métodos:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
class DebugMeta(type): """Metaclasse que debuga a criação de classes e instâncias.""" def __new__(mcs, nome, bases, dicionario): print(f"[__new__] Criando classe: {nome}") print(f" Bases: {bases}") print(f" Atributos: {list(dicionario.keys())}") # Adiciona um atributo de classe dicionario['criado_por'] = 'DebugMeta' return super().__new__(mcs, nome, bases, dicionario) def __init__(cls, nome, bases, dicionario): print(f"[__init__] Inicializando classe: {nome}") super().__init__(nome, bases, dicionario) def __call__(cls, *args, **kwargs): print(f"[__call__] Criando instância de {cls.__name__}") print(f" Args: {args}, Kwargs: {kwargs}") instancia = super().__call__(*args, **kwargs) print(f" Instância criada: {instancia}") return instancia class MinhaClasse(metaclass=DebugMeta): def __init__(self, valor): self.valor = valor print(f" __init__ da instância: valor={valor}") def metodo(self): return f"Valor: {self.valor}" print("=== Debug da Criação ===\n") print("Definindo a classe...") # A classe já é criada aqui print("\nCriando instância...") obj = MinhaClasse(42) print(f"\nResultado do método: {obj.metodo()}") print(f"Atributo de classe: {obj.criado_por}") # Metaclasse para verificar convenções de nomenclatura class NomenclaturaMeta(type): """Metaclasse que verifica nomes de métodos e atributos.""" def __new__(mcs, nome, bases, dicionario): # Verifica nomes de métodos (devem ser snake_case) for key in dicionario: if callable(dicionario[key]) and not key.startswith('__'): if '_' not in key and key != key.lower(): print(f"Aviso: Método '{key}' não segue snake_case") # Garante que a classe tenha docstring if not dicionario.get('__doc__'): dicionario['__doc__'] = f"Classe {nome} gerada automaticamente" return super().__new__(mcs, nome, bases, dicionario) class Calculadora(metaclass=NomenclaturaMeta): """Calculadora básica.""" def somar(self, a, b): return a + b def multiplicar(self, a, b): return a * b class CalculadoraRuim(metaclass=NomenclaturaMeta): def Somar(self, a, b): # Nome não segue convenção return a + b print("\n=== Validação de Nomenclatura ===") calc = Calculadora() print(f"Soma: {calc.somar(5, 3)}") # Metaclasse para adicionar logging automático class LoggingMeta(type): """Adiciona logging a todos os métodos da classe.""" def __new__(mcs, nome, bases, dicionario): for nome_metodo, metodo in dicionario.items(): if callable(metodo) and not nome_metodo.startswith('__'): # Envolve o método com logging def fazer_log(method): def logged_method(self, *args, **kwargs): print(f"[LOG] Chamando {method.__name__}") resultado = method(self, *args, **kwargs) print(f"[LOG] {method.__name__} retornou {resultado}") return resultado return logged_method dicionario[nome_metodo] = fazer_log(metodo) return super().__new__(mcs, nome, bases, dicionario) class Banco(metaclass=LoggingMeta): def depositar(self, valor): return f"Depositado R${valor:.2f}" def sacar(self, valor): return f"Sacado R${valor:.2f}" def saldo(self): return 1000.00 print("\n=== Logging Automático ===") banco = Banco() banco.depositar(500) banco.sacar(200) print(f"Saldo: R${banco.saldo():.2f}") |
Metaclasses operam no momento da definição da classe. A fórmula de profundidade da metaprogramação: \(M = \text{objeto} \to \text{classe} \to \text{metaclasse} \to \text{type}\) Metaclasses são ferramentas avançadas para situações específicas. Use-as com sabedoria e documente bem seu propósito. Para 99% dos casos, herança e composição são suficientes. Mas quando precisar de poder real, as metaclasses estão lá.