Módulo 11 - Metaclasses e descritores
__init_subclass__: o atalho para quase tudo
9 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026
O que você vai aprender
- Usar __init_subclass__ para reagir à criação de subclasses.
- Implementar registro automático de subclasses sem metaclass.
- Validar que subclasses cumprem um contrato ao serem definidas.
- Escolher entre __init_subclass__, decorador de classe e metaclass.
Ouvir o resumo desta aula
Um recap de cerca de 2 minutos na voz do Valim, para ouvir no trânsito ou na academia.
Ler a transcrição do resumo
Resumo da aula: __init_subclass__: o atalho para quase tudo.
Os objetivos desta aula. Usar __init_subclass__ para reagir à criação de subclasses. Implementar registro automático de subclasses sem metaclass. Validar que subclasses cumprem um contrato ao serem definidas. Escolher entre __init_subclass__, decorador de classe e metaclass.
Veja o essencial, parte por parte.
Reagir quando nasce uma subclasse. __init_subclass__ é chamado toda vez que a classe ganha uma subclasse nova.
Validar o contrato das subclasses. Outro uso comum é garantir que toda subclasse cumpra uma regra no momento em que é definida, e não mais tarde, quando um objeto for usado.
Escolhendo a ferramenta certa. Tudo é objeto, e classes são fabricadas por type: essa lente explica a linguagem.
Esse foi o resumo do essencial. Para se aprofundar, leia a aula completa e responda os exercícios.
Reagir quando nasce uma subclasse
Introduzido no Python 3.6, o __init_subclass__ é um dos ganchos mais úteis e menos conhecidos da linguagem. A ideia é simples: você define esse método na classe-mãe e, a partir daí, toda vez que alguém cria uma subclasse, o Python chama automaticamente o __init_subclass__ da mãe, passando a nova subclasse. É a mesma reação que o exemplo de registro com metaclass alcançava na segunda aula, só que sem a complexidade de escrever uma type personalizada. Para a maioria dos casos, é a resposta certa.
class Plugin:
registro = {}
def __init_subclass__(cls, /, nome=None, **kwargs):
super().__init_subclass__(**kwargs)
chave = nome or cls.__name__.lower()
Plugin.registro[chave] = cls
class ExportaCsv(Plugin, nome="csv"):
pass
class ExportaJson(Plugin):
pass
print(list(Plugin.registro)) # ['csv', 'exportajson']Registro automático de subclasses sem metaclass, com um argumento opcional na declaração.
Além de mais simples que a metaclass, este exemplo mostra um bônus: __init_subclass__ pode receber argumentos passados na própria declaração da subclasse. Repare em class ExportaCsv(Plugin, nome='csv'): esse nome='csv' chega ao método como parâmetro. É uma forma limpa de personalizar cada subclasse sem escrever código extra dentro dela. A subclasse ExportaJson, que não passou nome, cai no padrão baseado no nome da classe. Frameworks modernos preferem esse gancho justamente por ser legível.
Validar o contrato das subclasses
Outro uso comum é garantir que toda subclasse cumpra uma regra no momento em que é definida, e não mais tarde, quando um objeto for usado. Isso desloca o erro para o instante mais cedo possível, alinhado com a programação defensiva que o próximo módulo detalha. Se uma subclasse esquecer de definir um atributo obrigatório, o programa recusa a própria definição da classe, com uma mensagem clara, em vez de falhar de forma obscura horas depois.
class Comando:
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
if not hasattr(cls, "nome") or not isinstance(cls.nome, str):
raise TypeError(
f"{cls.__name__} precisa definir um atributo nome (texto)"
)
class Ajuda(Comando):
nome = "ajuda" # ok
try:
class Quebrado(Comando): # esqueceu o nome
pass
except TypeError as e:
print("recusado:", e) # recusado: Quebrado precisa definir um atributo nome (texto)O contrato é checado quando a subclasse é criada, falhando cedo e com clareza.
Este é o tipo de garantia que dá tranquilidade em bases de código maiores. A classe Ajuda passa porque define nome; a classe Quebrado é recusada no ato da definição, com uma mensagem que aponta exatamente o que falta. Sem esse gancho, o erro só apareceria quando alguém tentasse usar o comando, longe da causa. Com ele, o problema estoura ao lado do defeito. E, de novo, sem metaclass: um método na classe-mãe basta.
Escolhendo a ferramenta certa
| Objetivo | Ferramenta recomendada | Por quê |
|---|---|---|
| Reagir à criação de subclasses | __init_subclass__ | Simples, legível, sem metaclass |
| Modificar uma classe já criada | Decorador de classe | Explícito e fácil de ler |
| Controlar um atributo específico | Descritor ou property | Cirúrgico e reaproveitável |
| Controlar a criação de toda uma família de classes | Metaclass | Último recurso, poder total |
Um guia de decisão: metaclass é o último recurso, não o primeiro.
Esta tabela resume a mensagem do módulo. Diante de um problema de metaprogramação, percorra as opções de cima para baixo. Precisa reagir a subclasses? __init_subclass__. Precisa ajustar uma classe pronta? Um decorador de classe. Precisa mediar o acesso a um atributo? Um descritor ou property. Só quando nenhuma dessas serve, e você consegue justificar por escrito, uma metaclass entra em cena. Essa disciplina mantém o código legível para quem vem depois, que é o verdadeiro teste de bom código.
Teste rápido
Você quer registrar automaticamente cada subclasse de uma classe-base. Qual a primeira escolha?
Perguntas frequentes
- Desde quando o __init_subclass__ existe?
- Desde o Python 3.6, introduzido para cobrir de forma simples muitos casos que antes só uma metaclass resolvia. Por ser mais legível e menos arriscado, tornou-se a escolha preferida para reagir à criação de subclasses em código moderno.
- Preciso decorar __init_subclass__ com @classmethod?
- Não é obrigatório: o Python já o trata como método de classe implicitamente, passando a subclasse recém-criada como cls. Você pode escrever @classmethod para deixar a intenção explícita, e isso não muda o comportamento. O importante é não esquecer o super().__init_subclass__.
- Por que chamar super().__init_subclass__ dentro do método?
- Para cooperar com a cadeia de herança. Se houver outras classes-base com o próprio __init_subclass__, chamar super() garante que todas participem. Omitir essa chamada pode quebrar bibliotecas que dependem do gancho. É uma boa prática seguir sempre esse padrão.
- Qual a diferença entre __init_subclass__ e um decorador de classe?
- __init_subclass__ vive na classe-mãe e age automaticamente em toda subclasse, sem quem escreve a subclasse precisar lembrar de nada. Um decorador de classe é aplicado explicitamente, classe a classe. Use o gancho para comportamento herdado por toda a família; use o decorador para ajustes pontuais.
- Então quando eu vou realmente precisar de uma metaclass?
- Em situações de biblioteca que exigem controle total sobre como classes são criadas, como ORMs que traduzem atributos em colunas ou sistemas que precisam alterar o namespace antes de a classe existir. Para código de aplicação, é raro. A regra do módulo permanece: metaclass por último.
Fontes
Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.