Escritura de metaclases personalizadas
Utilice __new__ y __init__ en metaclases para personalizar la creación de clases.
Escritura de metaclases personalizadas es una lección gratuita de Python Academy en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Python Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Python Academy incluye 4 lecciones en total.
Declaración de una metaclase
Incluya metaclass=MyMeta en la definición de la clase para usar una metaclase personalizada. La metaclase debe heredar de type.
class MyMeta(type):
pass
class Foo(metaclass=MyMeta):
pass
print(type(Foo)) # <class '__main__.MyMeta'>Sobrescritura de __new__
__new__(mcs, name, bases, namespace) crea el objeto de clase. Úselo para modificar el espacio de nombres antes de construir la clase.
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) # TrueSobrescritura de __init__
__init__(cls, name, bases, namespace) se ejecuta después de crear la clase. Úselo para tareas posteriores a la creación, como el 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': ...}Imposición de contratos de interfaz
Genere TypeError en __new__ si faltan métodos necesarios en el cuerpo de la clase.
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 mediante una metaclase
Sobrescriba __call__ en la metaclase para interceptar la creación de instancias y devolver siempre la misma instancia.
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()) # TrueAdición dinámica de métodos
Inyecte métodos de utilidad en cada clase que use la metaclase, sin modificar cada clase 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 un espacio de nombres ordenado
Sobrescriba __prepare__ para proporcionar un mapeo personalizado como espacio de nombres de la clase, por ejemplo, para conservar el orden de definición o detectar duplicados.
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()Resolución de conflictos entre metaclases
Cuando varias clases base tienen metaclases diferentes, Python exige usar explícitamente la metaclase más derivada o genera un error de conflicto.
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): passMetaclase frente a __init_subclass__
Para registrar subclases y crear hooks sencillos, __init_subclass__ es más simple y no requiere una metaclase.
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]Seguimiento del acceso a atributos
Sobrescriba __getattribute__ en la metaclase para interceptar el acceso a atributos de la clase, no de las instancias.
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: HOSTPatrón de recopilación de campos de ORM
Los ORM al estilo de Django usan metaclases para recopilar los descriptores Field del cuerpo de la clase en un 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'])Comprobación rápida
En una metaclase personalizada, ¿qué método se llama para crear el propio objeto de clase antes de __init__?
Recapitulación
Las metaclases personalizadas sobrescriben __new__/__init__ para controlar la creación de clases: imponen contratos, registran subclases automáticamente, inyectan métodos o recopilan descriptores de campos. Prefiera __init_subclass__ para necesidades sencillas de registro.
Preguntas frecuentes
¿La lección «Escritura de metaclases personalizadas» es gratis?
Sí — el texto completo de «Escritura de metaclases personalizadas» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Python Academy, actualiza a CoddyKit PRO. El curso de Python Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Escritura de metaclases personalizadas»?
Utilice __new__ y __init__ en metaclases para personalizar la creación de clases. Practicas Python Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Python Academy?
No se requiere experiencia previa. Python Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.
¿Cuánto tiempo toma la lección «Escritura de metaclases personalizadas»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Python Academy?
Sí. Cada lección de Python Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Cómo se crean las clases de Python
- Escritura de metaclases personalizadas
- Descriptores: __get__, __set__, __delete__
- __slots__ y optimización de memoria