Módulo 11 - Metaclasses e descritores

O que é uma metaclass

10 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026

O que você vai aprender

  • Definir metaclass como uma subclasse de type.
  • Escrever uma metaclass com __new__ e __init__ que intercepta a criação da classe.
  • Usar o argumento metaclass= na declaração da classe.
  • Reconhecer os poucos casos em que uma metaclass se justifica.

Uma classe que cria classes

Na aula anterior você viu que type fabrica todas as classes. Uma metaclass é apenas uma type sua, personalizada. Como a metaclass é a fábrica, ela é chamada no exato momento em que uma classe é criada, e não quando um objeto dessa classe é instanciado. Essa é a distinção que confunde no começo: uma classe comum controla o nascimento dos objetos; uma metaclass controla o nascimento das classes. É um andar acima.

class MinhaMeta(type):
    def __new__(mcs, nome, bases, namespace):
        print(f"criando a classe {nome}")
        return super().__new__(mcs, nome, bases, namespace)

class Produto(metaclass=MinhaMeta):
    preco = 10

# Saida na hora de definir a classe: criando a classe Produto

p = Produto()   # aqui nao imprime nada: a metaclass ja rodou antes

A metaclass roda quando Produto é definida, não quando um Produto é instanciado.

Repare que a mensagem aparece assim que a classe Produto é declarada, e não quando p = Produto() é executado. Isso comprova o ponto: a metaclass age na criação da classe. Os parâmetros de __new__ são reveladores: nome é o texto do nome da classe, bases é a tupla de classes-mãe e namespace é o dicionário com tudo que foi escrito no corpo da classe, incluindo métodos e atributos como preco. É esse dicionário que você pode inspecionar ou alterar antes de a classe existir de fato.

__new__ e __init__ da metaclass

Uma metaclass tem dois pontos de entrada, e vale separar o papel de cada um. O __new__ cria o objeto-classe e é onde você inspeciona ou modifica o namespace antes de a classe passar a existir. O __init__ recebe a classe já criada e serve para ajustes finais ou registros. Uma metáfora simples: __new__ é a linha de montagem que fabrica a classe; __init__ é a inspeção de qualidade que acontece com a classe pronta na mão.

class RegistroMeta(type):
    registro = {}

    def __init__(cls, nome, bases, namespace):
        super().__init__(nome, bases, namespace)
        # A classe ja existe aqui; guardamos numa tabela central.
        if bases:  # ignora a propria base abstrata
            RegistroMeta.registro[nome] = cls

class Base(metaclass=RegistroMeta):
    pass

class Boleto(Base):
    pass

class Pix(Base):
    pass

print(list(RegistroMeta.registro))  # ['Boleto', 'Pix']

Um caso real: registrar automaticamente toda subclasse numa tabela central.

Este exemplo é um dos usos legítimos de metaclass: um registro automático. Toda vez que alguém cria uma subclasse de Base, a metaclass a guarda numa tabela, sem que o autor da subclasse precise lembrar de registrar. Frameworks de plugins e serializadores usam essa técnica. Ainda assim, guarde a ressalva: como você verá na última aula do módulo, o gancho __init_subclass__ resolve muitos desses casos de forma mais simples, sem escrever uma metaclass.

Quando (raramente) usar

Essa frase, sobre não precisar de metaclass quando se está em dúvida, é um conselho clássico da comunidade Python e vale levar a sério. Metaclasses são poderosas porque interceptam a criação de toda uma família de classes, mas esse mesmo poder torna o código difícil de entender para quem chega depois. Os casos que de fato pedem metaclass são de biblioteca: ORMs que transformam atributos de classe em colunas de banco, sistemas que impõem uma interface a todas as subclasses, ferramentas que precisam do controle mais profundo possível.

Para código de aplicação, quase sempre há um caminho mais leve. Precisa reagir quando uma classe é criada como filha de outra? __init_subclass__ resolve. Precisa modificar uma classe depois de pronta? Um decorador de classe basta e é mais legível. Precisa controlar o acesso a um atributo? Um descritor, tema da próxima aula, é a ferramenta certa. Reserve a metaclass para quando nenhuma dessas alternativas der conta, e documente bem o porquê. O objetivo do módulo não é te transformar em autor de metaclasses, é te dar a leitura correta quando encontrar uma.

Teste rápido

Quando os métodos __new__ e __init__ de uma metaclass são executados?

Perguntas frequentes

Qual a diferença entre metaclass e herança?
Herança define de onde uma classe reaproveita atributos e métodos; é uma relação entre classes irmãs de nível. Metaclass define quem fabrica a classe, um nível acima. Uma classe pode herdar de várias bases e, ao mesmo tempo, ter uma metaclass que controla como ela nasce.
Toda classe tem uma metaclass?
Sim. Se você não especifica, a metaclass é type, a fábrica padrão. Quando você escreve metaclass=OutraCoisa, apenas troca a fábrica por uma personalizada. Por isso dizemos que type é a metaclass padrão de todas as classes.
Posso usar duas metaclasses diferentes com herança múltipla?
Não livremente. Se duas bases têm metaclasses incompatíveis, o Python levanta um erro de conflito de metaclass. A metaclass da classe filha precisa ser a mesma das bases ou uma subclasse delas. É mais uma razão para usar metaclasses com parcimônia.
__new__ ou __init__ na metaclass, qual escolher?
Use __new__ quando precisar inspecionar ou modificar o namespace antes de a classe existir, por exemplo alterando atributos. Use __init__ quando a classe já criada bastar, como em registros e ajustes finais. Se não precisa mudar a classe antes de criá-la, __init__ é mais simples.
Existe alternativa mais simples que quase sempre serve?
Sim: o gancho __init_subclass__, visto na última aula do módulo, cobre a maioria dos casos de reagir à criação de subclasses sem escrever metaclass. Decoradores de classe e descritores cobrem outros. Metaclass fica para o que nenhum deles resolve.

Fontes

Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.