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) # TrueSubstituindo __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()) # TrueAdicionando 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): passMetaclasse 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: HOSTPadrã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
- Como as classes Python são criadas
- Escrita de metaclasses personalizadas
- Descritores: __get__, __set__, __delete__
- __slots__ e otimização de memória