Python

Como Usar Metaclasses em Python: type, __new__ e Controle de Criação de Classes em Produção

13 min de leitura

Como Usar Metaclasses em Python: type, __new__ e Controle de Criação de Classes em Produção

O que são Metaclasses? Uma metaclasse é uma classe cujas instâncias são classes. Essa definição pode parecer abstrata à primeira vista, mas é fundamental entender que em Python, tudo é um objeto — inclusive as classes. Quando você define uma classe usando a palavra-chave , você está, na verdade, criando uma instância de uma metaclasse. Por padrão, essa metaclasse é . Para compreender melhor, considere que se um objeto é uma instância de uma classe, então uma classe é uma instância de uma metaclasse. Isso significa que você pode controlar como uma classe é criada, quais atributos ela tem e como ela se comporta, usando metaclasses. É como ter um "construtor de construtores" — você controla a criação de classes da mesma forma que construtores controlam a criação de objetos. Entendendo a Função type() type() como Inspecionador A função tem dois comportamentos distintos. Quando chamada com um argumento, ela retorna a metaclasse (o tipo) do objeto passado. Quando chamada com

<h2>O que são Metaclasses?</h2>

<p>Uma metaclasse é uma classe cujas instâncias são classes. Essa definição pode parecer abstrata à primeira vista, mas é fundamental entender que em Python, tudo é um objeto — inclusive as classes. Quando você define uma classe usando a palavra-chave <code>class</code>, você está, na verdade, criando uma instância de uma metaclasse. Por padrão, essa metaclasse é <code>type</code>.</p>

<p>Para compreender melhor, considere que se um objeto é uma instância de uma classe, então uma classe é uma instância de uma metaclasse. Isso significa que você pode controlar como uma classe é criada, quais atributos ela tem e como ela se comporta, usando metaclasses. É como ter um &quot;construtor de construtores&quot; — você controla a criação de classes da mesma forma que construtores controlam a criação de objetos.</p>

<pre><code class="language-python">class Pessoa:

def __init__(self, nome):

self.nome = nome

Verificar a metaclasse de Pessoa

print(type(Pessoa)) # &lt;class &#039;type&#039;&gt;

print(isinstance(Pessoa, type)) # True

Pessoa é uma instância de type

joao = Pessoa(&quot;João&quot;)

print(type(joao)) # &lt;class &#039;__main__.Pessoa&#039;&gt;</code></pre>

<h2>Entendendo a Função type()</h2>

<h3>type() como Inspecionador</h3>

<p>A função <code>type()</code> tem dois comportamentos distintos. Quando chamada com um argumento, ela retorna a metaclasse (o tipo) do objeto passado. Quando chamada com três argumentos, ela cria uma classe dinamicamente. Começaremos pelo primeiro uso, que é mais simples e didático.</p>

<pre><code class="language-python">class Animal:

pass

type() inspecionando um objeto

numero = 42

print(type(numero)) # &lt;class &#039;int&#039;&gt;

lista = [1, 2, 3]

print(type(lista)) # &lt;class &#039;list&#039;&gt;

type() inspecionando uma classe

print(type(Animal)) # &lt;class &#039;type&#039;&gt;

print(type(int)) # &lt;class &#039;type&#039;&gt;

print(type(str)) # &lt;class &#039;type&#039;&gt;</code></pre>

<p>Observe que todas as classes têm <code>type</code> como sua metaclasse. Isso é o padrão em Python. Mas aqui está o ponto crucial: você pode criar suas próprias metaclasses que herdam de <code>type</code> para personalizar o comportamento da criação de classes.</p>

<h3>type() como Construtor de Classes</h3>

<p>O segundo comportamento de <code>type()</code> permite criar classes programaticamente. A sintaxe é <code>type(nome, bases, dicionário)</code>, onde <code>nome</code> é uma string com o nome da classe, <code>bases</code> é uma tupla com as classes pai, e <code>dicionário</code> é um dicionário com os atributos e métodos da classe.</p>

<pre><code class="language-python"># Criar uma classe dinamicamente usando type()

Veiculo = type(&#039;Veiculo&#039;, (), {

&#039;velocidade_maxima&#039;: 200,

&#039;acelerar&#039;: lambda self: print(&quot;Acelerando!&quot;)

})

Usar a classe criada

carro = Veiculo()

print(carro.velocidade_maxima) # 200

carro.acelerar() # Acelerando!

Criar uma classe com herança

class Motor:

def ligar(self):

return &quot;Motor ligado&quot;

Carro = type(&#039;Carro&#039;, (Motor,), {

&#039;marca&#039;: &#039;Toyota&#039;,

&#039;modelo&#039;: &#039;Corolla&#039;,

&#039;velocidade_maxima&#039;: 220

})

meu_carro = Carro()

print(meu_carro.ligar()) # Motor ligado

print(meu_carro.marca) # Toyota</code></pre>

<h2>Criando Metaclasses Personalizadas</h2>

<h3>O Método __new__ em Metaclasses</h3>

<p>Quando você cria uma metaclasse personalizada, herda de <code>type</code> e sobrescreve o método <code>__new__</code>. Este método é responsável pela criação da classe. A diferença entre <code>__new__</code> e <code>__init__</code> é que <code>__new__</code> cria o objeto (neste caso, a classe), enquanto <code>__init__</code> o inicializa após a criação. Em metaclasses, você geralmente trabalha com <code>__new__</code> porque quer controlar a estrutura da classe antes dela existir completamente.</p>

<pre><code class="language-python">class MinhaMetaclasse(type):

def __new__(cls, nome, bases, dicionario):

print(f&quot;Criando classe: {nome}&quot;)

Aqui você pode modificar o dicionário antes da classe ser criada

dicionario[&#039;classe_criada_em&#039;] = &#039;MinhaMetaclasse&#039;

Chamar o __new__ da metaclasse pai (type)

nova_classe = super().__new__(cls, nome, bases, dicionario)

return nova_classe

Usar a metaclasse personalizada

class Produto(metaclass=MinhaMetaclasse):

def __init__(self, nome):

self.nome = nome

Ao executar, verá: &quot;Criando classe: Produto&quot;

print(Produto.classe_criada_em) # MinhaMetaclasse

produto = Produto(&quot;Notebook&quot;)

print(produto.nome) # Notebook</code></pre>

<h3>Controlando Atributos e Métodos</h3>

<p>Uma aplicação prática de metaclasses é validar e controlar quais atributos e métodos uma classe pode ter. Por exemplo, você pode forçar que todos os métodos de uma classe tenham docstrings, ou que todos os atributos privados sigam uma convenção específica.</p>

<pre><code class="language-python">class ValidadorDocstring(type):

def __new__(cls, nome, bases, dicionario):

Verificar se todos os métodos têm docstring

for chave, valor in dicionario.items():

if callable(valor) and not chave.startswith(&#039;_&#039;):

if not valor.__doc__:

raise TypeError(f&quot;Método &#039;{chave}&#039; deve ter docstring&quot;)

return super().__new__(cls, nome, bases, dicionario)

Esta classe será criada com sucesso

class APIBemDocumentada(metaclass=ValidadorDocstring):

def get_usuarios(self):

&quot;&quot;&quot;Retorna lista de usuários&quot;&quot;&quot;

return []

def criar_usuario(self, nome):

&quot;&quot;&quot;Cria um novo usuário&quot;&quot;&quot;

return {&#039;nome&#039;: nome}

Esta classe vai gerar erro

try:

class APIMalDocumentada(metaclass=ValidadorDocstring):

def get_usuarios(self):

return [] # Sem docstring!

except TypeError as e:

print(f&quot;Erro: {e}&quot;) # Erro: Método &#039;get_usuarios&#039; deve ter docstring</code></pre>

<h3>Transformando Métodos Automaticamente</h3>

<p>Outro caso de uso real é transformar ou decorar todos os métodos de uma classe automaticamente. Por exemplo, adicionar logging a todos os métodos ou validação de argumentos.</p>

<pre><code class="language-python">import functools

import time

class LogMetodos(type):

def __new__(cls, nome, bases, dicionario):

for chave, valor in dicionario.items():

if callable(valor) and not chave.startswith(&#039;_&#039;):

Decorar cada método com logging

dicionario[chave] = cls._adicionar_logging(valor)

return super().__new__(cls, nome, bases, dicionario)

@staticmethod

def _adicionar_logging(funcao):

@functools.wraps(funcao)

def wrapper(args, *kwargs):

inicio = time.time()

print(f&quot;[LOG] Chamando {funcao.__name__}&quot;)

resultado = funcao(args, *kwargs)

tempo = time.time() - inicio

print(f&quot;[LOG] {funcao.__name__} levou {tempo:.4f}s&quot;)

return resultado

return wrapper

class CalculadoraComLog(metaclass=LogMetodos):

def somar(self, a, b):

time.sleep(0.1)

return a + b

def multiplicar(self, a, b):

time.sleep(0.05)

return a * b

calc = CalculadoraComLog()

print(calc.somar(5, 3))

print(calc.multiplicar(4, 2))

Saída:

[LOG] Chamando somar

[LOG] somar levou 0.1002s

8

[LOG] Chamando multiplicar

[LOG] multiplicar levou 0.0503s

8</code></pre>

<h2>Controle Avançado com __init__ e __call__</h2>

<h3>Inicialização de Metaclasses com __init__</h3>

<p>Além de <code>__new__</code>, você pode sobrescrever <code>__init__</code> em uma metaclasse para fazer processamento após a classe ser criada. Enquanto <code>__new__</code> controla a criação da classe, <code>__init__</code> pode fazer validações ou configurações adicionais.</p>

<pre><code class="language-python">class ConfiguradorClasse(type):

def __new__(cls, nome, bases, dicionario):

dicionario[&#039;versao&#039;] = &#039;1.0&#039;

return super().__new__(cls, nome, bases, dicionario)

def __init__(cls, nome, bases, dicionario):

super().__init__(nome, bases, dicionario)

Registrar a classe criada

print(f&quot;Classe {nome} foi inicializada com versão {cls.versao}&quot;)

class MinhaClasse(metaclass=ConfiguradorClasse):

pass

Saída: Classe MinhaClasse foi inicializada com versão 1.0</code></pre>

<h3>Controlando a Instanciação com __call__</h3>

<p>O método <code>__call__</code> de uma metaclasse controla o que acontece quando você tenta criar uma instância da classe. Isso permite implementar padrões como Singleton ou Factory Methods no nível de metaclasse.</p>

<pre><code class="language-python">class Singleton(type):

_instancias = {}

def __call__(cls, args, *kwargs):

Se a classe já foi instanciada, retornar a instância existente

if cls not in cls._instancias:

cls._instancias[cls] = super().__call__(args, *kwargs)

return cls._instancias[cls]

class ConexaoDB(metaclass=Singleton):

def __init__(self):

self.id = id(self)

print(f&quot;Conexão criada com ID: {self.id}&quot;)

Criar duas &quot;instâncias&quot;

conn1 = ConexaoDB()

print(f&quot;conn1 ID: {conn1.id}&quot;)

conn2 = ConexaoDB()

print(f&quot;conn2 ID: {conn2.id}&quot;)

print(f&quot;Mesma instância? {conn1 is conn2}&quot;) # True

Saída:

Conexão criada com ID: 140234567890

conn1 ID: 140234567890

conn2 ID: 140234567890

Mesma instância? True</code></pre>

<p>Outro exemplo prático é validar os argumentos passados ao criar uma instância:</p>

<pre><code class="language-python">class ValidadorArgumentos(type):

def __call__(cls, args, *kwargs):

Validar antes de criar a instância

if hasattr(cls, &#039;validar_argumentos&#039;):

cls.validar_argumentos(args, *kwargs)

Chamar o __new__ e __init__ normalmente

return super().__call__(args, *kwargs)

class Usuario(metaclass=ValidadorArgumentos):

def __init__(self, nome, idade):

self.nome = nome

self.idade = idade

@staticmethod

def validar_argumentos(nome, idade):

if not isinstance(nome, str) or not nome:

raise ValueError(&quot;Nome deve ser uma string não vazia&quot;)

if not isinstance(idade, int) or idade &lt; 0:

raise ValueError(&quot;Idade deve ser um inteiro positivo&quot;)

Funciona

usuario = Usuario(&quot;Alice&quot;, 30)

print(f&quot;{usuario.nome} tem {usuario.idade} anos&quot;)

Gera erro

try:

usuario_invalido = Usuario(&quot;Bob&quot;, -5)

except ValueError as e:

print(f&quot;Erro: {e}&quot;) # Erro: Idade deve ser um inteiro positivo</code></pre>

<h2>Casos de Uso Reais e Boas Práticas</h2>

<h3>Implementando um ORM Simplificado</h3>

<p>Um caso real de metaclasses é na criação de ORMs (Object-Relational Mapping). Frameworks como Django usam metaclasses para transformar definições de classes em modelos de banco de dados.</p>

<pre><code class="language-python">class CampoDB:

def __init__(self, tipo):

self.tipo = tipo

def __repr__(self):

return f&quot;CampoDB({self.tipo})&quot;

class Modelo(type):

def __new__(cls, nome, bases, dicionario):

campos = {}

Encontrar todos os campos definidos

for chave, valor in list(dicionario.items()):

if isinstance(valor, CampoDB):

campos[chave] = valor

Armazenar os campos na classe

dicionario[&#039;_campos&#039;] = campos

Remover os objetos CampoDB do dicionário

for chave in campos:

del dicionario[chave]

return super().__new__(cls, nome, bases, dicionario)

class Usuario(metaclass=Modelo):

nome = CampoDB(&#039;string&#039;)

idade = CampoDB(&#039;inteiro&#039;)

email = CampoDB(&#039;string&#039;)

print(Usuario._campos)

{&#039;nome&#039;: CampoDB(string), &#039;idade&#039;: CampoDB(inteiro), &#039;email&#039;: CampoDB(string)}

Você poderia então usar isso para gerar SQL ou validar dados

for campo, tipo in Usuario._campos.items():

print(f&quot;CREATE COLUMN {campo} {tipo.tipo}&quot;)</code></pre>

<h3>Quando NÃO Usar Metaclasses</h3>

<p>Uma consideração importante: metaclasses são poderosas, mas complexas. Use-as apenas quando realmente necessário. Alternativas como decoradores de classe e herança normal resolvem a maioria dos problemas de forma mais clara e manutenível.</p>

<pre><code class="language-python"># ❌ Evitar metaclasses complexas quando um decorador resolve:

def adicionar_logging(cls):

&quot;&quot;&quot;Decorador é mais legível que uma metaclasse&quot;&quot;&quot;

for atributo in dir(cls):

if callable(getattr(cls, atributo)) and not atributo.startswith(&#039;_&#039;):

setattr(cls, atributo, _log_wrapper(getattr(cls, atributo)))

return cls

def _log_wrapper(func):

def wrapper(args, *kwargs):

print(f&quot;Chamando {func.__name__}&quot;)

return func(args, *kwargs)

return wrapper

@adicionar_logging

class MinhaClasse:

def metodo(self):

return &quot;resultado&quot;</code></pre>

<h2>Conclusão</h2>

<p>Três pontos fundamentais foram abordados neste artigo. Primeiro, compreender que metaclasses são classes que criam classes — um conceito diferente de herança normal — é essencial para dominar a programação avançada em Python. Segundo, o método <code>__new__</code> em metaclasses oferece controle fino sobre como as classes são estruturadas antes de sua existência completa, permitindo validações, transformações e adição de atributos automaticamente. Terceiro, embora metaclasses sejam ferramentas poderosas usadas em frameworks profissionais, devem ser usadas com parcimônia — decoradores e composição frequentemente oferecem soluções mais legíveis e manuteníveis.</p>

<p>Dominar metaclasses o preparará para estudar frameworks como Django, compreender internamente como bibliotecas funcionam e, mais importante, saber quando aplicar esses conceitos avançados para resolver problemas reais de forma elegante.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://docs.python.org/3/reference/datamodel.html#metaclasses" target="_blank" rel="noopener noreferrer">Python Documentation: Metaclasses</a></li>

<li><a href="https://docs.python.org/3/library/functions.html#type" target="_blank" rel="noopener noreferrer">Python Documentation: type() built-in</a></li>

<li><a href="https://realpython.com/python-metaclasses/" target="_blank" rel="noopener noreferrer">Real Python: Metaclasses in Python</a></li>

<li><a href="https://www.python.org/dev/peps/pep-3115/" target="_blank" rel="noopener noreferrer">PEP 3115 - Metaclass Syntax in Python 3</a></li>

<li><a href="https://www.oreilly.com/library/view/fluent-python-2nd/9781492126249/" target="_blank" rel="noopener noreferrer">Fluent Python - Luciano Ramalho - Capítulo sobre Metaclasses</a></li>

</ul>

<p>&lt;!-- FIM --&gt;</p>

Comentários

Mais em Python

Tipos Avançados em Python: Generic, Protocol, TypeVar e ParamSpec na Prática
Tipos Avançados em Python: Generic, Protocol, TypeVar e ParamSpec na Prática

Introdução aos Tipos Avançados em Python Python é uma linguagem dinamicamente...

Guia Completo de Módulos e Pacotes em Python: import, __init__ e Organização de Projetos
Guia Completo de Módulos e Pacotes em Python: import, __init__ e Organização de Projetos

Entendendo o Sistema de Módulos e Pacotes em Python Um módulo em Python é sim...

O que Todo Dev Deve Saber sobre Programação Funcional em Python: map, filter, functools e itertools
O que Todo Dev Deve Saber sobre Programação Funcional em Python: map, filter, functools e itertools

Fundamentos da Programação Funcional em Python A programação funcional é um p...