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.

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

ObjetivoFerramenta recomendadaPor quê
Reagir à criação de subclasses__init_subclass__Simples, legível, sem metaclass
Modificar uma classe já criadaDecorador de classeExplícito e fácil de ler
Controlar um atributo específicoDescritor ou propertyCirúrgico e reaproveitável
Controlar a criação de toda uma família de classesMetaclassÚ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.