0Pricing
Python Academy · Aula

Escrita de metaclasses personalizadas

Use __new__ e __init__ em metaclasses para personalizar a criação de classes.

Escrita de metaclasses personalizadas é uma aula grátis de Python Academy no CoddyKit. Esta é a aula 2 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Python Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Python Academy inclui 4 aulas no total.

Declarando uma metaclasse

Passe metaclass=MyMeta na definição da classe para usar uma metaclasse personalizada. A metaclasse deve herdar de type.

class MyMeta(type):
    pass

class Foo(metaclass=MyMeta):
    pass

print(type(Foo))   # <class '__main__.MyMeta'>

Substituindo __new__

__new__(mcs, name, bases, namespace) cria o objeto de classe. Use-o para modificar o espaço de nomes antes de a classe ser construída.

class UpperAttrMeta(type):
    def __new__(mcs, name, bases, namespace):
        upper = {k.upper(): v for k, v in namespace.items()
                 if not k.startswith("_")}
        return super().__new__(mcs, name, bases, {**namespace, **upper})

class Config(metaclass=UpperAttrMeta):
    debug = True

print(Config.DEBUG)  # True

Substituindo __init__

__init__(cls, name, bases, namespace) é executado depois que a classe é criada. Use-o para configurações posteriores à criação, como o registro.

_registry = {}

class AutoRegisterMeta(type):
    def __init__(cls, name, bases, namespace):
        super().__init__(name, bases, namespace)
        if bases:   # skip the base class itself
            _registry[name] = cls

class Handler(metaclass=AutoRegisterMeta): pass
class JSONHandler(Handler): pass
class XMLHandler(Handler):  pass

print(_registry)  # {'JSONHandler': ..., 'XMLHandler': ...}

Impondo contratos de interface

Gere TypeError em __new__ se os métodos necessários estiverem ausentes do corpo da classe.

class InterfaceMeta(type):
    REQUIRED = frozenset({"process", "validate"})

    def __new__(mcs, name, bases, ns):
        if bases and not mcs.REQUIRED.issubset(ns):
            missing = mcs.REQUIRED - ns.keys()
            raise TypeError(f"{name} must define: {missing}")
        return super().__new__(mcs, name, bases, ns)

class Worker(metaclass=InterfaceMeta):
    def process(self): ...
    def validate(self): ...

Singleton por meio de metaclasse

Substitua __call__ na metaclasse para interceptar a criação de instâncias e retornar sempre a mesma instância.

class SingletonMeta(type):
    _instances = {}
    def __call__(cls, *a, **kw):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*a, **kw)
        return cls._instances[cls]

class DB(metaclass=SingletonMeta):
    pass

print(DB() is DB())  # True

Adicionando métodos dinamicamente

Injete métodos utilitários em toda classe que use a metaclasse, sem modificar cada classe individualmente.

class MethodInjectorMeta(type):
    def __new__(mcs, name, bases, ns):
        ns["describe"] = lambda self: f"I am {type(self).__name__}"
        return super().__new__(mcs, name, bases, ns)

class Widget(metaclass=MethodInjectorMeta):
    pass

print(Widget().describe())   # I am Widget

__prepare__ para espaço de nomes ordenado

Substitua __prepare__ para fornecer um mapeamento personalizado como espaço de nomes da classe, por exemplo, para preservar a ordem das definições ou detectar duplicatas.

class NoDuplicatesMeta(type):
    class _NS(dict):
        def __setitem__(self, k, v):
            if k in self and not k.startswith("_"):
                raise ValueError(f"Duplicate attribute: {k}")
            super().__setitem__(k, v)

    @classmethod
    def __prepare__(mcs, name, bases):
        return mcs._NS()

Resolução de conflitos de metaclasses

Quando várias classes base têm metaclasses diferentes, o Python exige que a metaclasse mais derivada seja usada explicitamente; caso contrário, um erro de conflito é gerado.

class MetaA(type): pass
class MetaB(type): pass

# class Bad(A, B): pass  # TypeError: metaclass conflict

class MetaAB(MetaA, MetaB): pass   # resolve manually

class A(metaclass=MetaA): pass
class B(metaclass=MetaB): pass
class Good(A, B, metaclass=MetaAB): pass

Metaclasse versus __init_subclass__

Para registrar subclasses e criar ganchos simples, __init_subclass__ é mais simples e não exige uma metaclasse.

class Base:
    _registry = []
    def __init_subclass__(cls, **kw):
        super().__init_subclass__(**kw)
        Base._registry.append(cls)

class ChildA(Base): pass
class ChildB(Base): pass

print(Base._registry)  # [ChildA, ChildB]

Rastreando o acesso a atributos

Substitua __getattribute__ na metaclasse para interceptar o acesso a atributos na classe (não nas instâncias).

class TraceMeta(type):
    def __getattribute__(cls, name):
        val = super().__getattribute__(name)
        if not name.startswith("_"):
            print(f"Accessing class attr: {name}")
        return val

class Config(metaclass=TraceMeta):
    HOST = "localhost"

_ = Config.HOST   # logs: Accessing class attr: HOST

Padrão de coleta de campos de ORM

Os ORMs no estilo Django usam metaclasses para coletar descritores Field do corpo da classe em um registro _meta.

class Field:
    def __init__(self, col): self.col = col

class ModelMeta(type):
    def __new__(mcs, name, bases, ns):
        fields = {k: v for k, v in ns.items() if isinstance(v, Field)}
        ns["_fields"] = fields
        return super().__new__(mcs, name, bases, ns)

class User(metaclass=ModelMeta):
    name = Field("name")
    email = Field("email")

print(User._fields.keys())  # dict_keys(['name', 'email'])

Verificação rápida

Em uma metaclasse personalizada, qual método é chamado para criar o próprio objeto de classe (antes de __init__)?

Recapitulação

Metaclasses personalizadas substituem __new__/__init__ para controlar a criação de classes: impor contratos, registrar subclasses automaticamente, injetar métodos ou coletar descritores de campos. Prefira __init_subclass__ para necessidades simples de registro.

Perguntas Frequentes

A aula “Escrita de metaclasses personalizadas” é grátis?

Sim — o texto completo de “Escrita de metaclasses personalizadas” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Python Academy, atualize para CoddyKit PRO. O curso de Python Academy inclui 4 aulas no total.

O que vou aprender em “Escrita de metaclasses personalizadas”?

Use __new__ e __init__ em metaclasses para personalizar a criação de classes. Você pratica Python Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Python Academy?

Nenhuma experiência prévia é necessária. Python Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 4.

Quanto tempo leva a aula “Escrita de metaclasses personalizadas”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Python Academy?

Sim. Cada aula de Python Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Como as classes Python são criadas
  2. Escrita de metaclasses personalizadas
  3. Descritores: __get__, __set__, __delete__
  4. __slots__ e otimização de memória
← Voltar para Python Academy