Los metaclases en Python son clases especiales que crean y gestionan otras clases. Como en Python todo es un objeto, incluidas las clases, los metaclases pueden considerarse "clases para clases". Proporcionan un mecanismo para controlar el proceso de creación de clases y modificar su comportamiento en el momento de la definición.
Cómo funcionan los metaclases
Cuando Python encuentra la definición de una clase, automáticamente usa el metaclase type para crearla. El proceso es el siguiente:
class MiClase:
pass
Internamente, Python ejecuta:
MiClase = type('MiClase', (), {})
Donde:
'MiClase'— el nombre de la clase()— la tupla de clases padre{}— el diccionario de atributos y métodos de la clase
El metaclase incorporado type es el responsable de crear todas las clases en Python por defecto.
Crear tus propios metaclases
Los metaclases personalizados se crean heredando de type y sobrescribiendo sus métodos:
class MiMeta(type):
def __new__(cls, name, bases, attrs):
print(f"Creando la clase {name}")
attrs['creado_por_metaclase'] = True
return super().__new__(cls, name, bases, attrs)
class MiClase(metaclass=MiMeta):
pass
print(MiClase.creado_por_metaclase) # True
Métodos clave de los metaclases
__new__
Controla la creación de la clase. Se llama antes de __init__ y devuelve la nueva clase:
class MetaCreacion(type):
def __new__(cls, name, bases, attrs):
# Modificación de atributos antes de crear la clase
attrs['id_clase'] = id(cls)
return super().__new__(cls, name, bases, attrs)
__init__
Inicializa la clase ya creada:
class MetaInit(type):
def __init__(cls, name, bases, attrs):
print(f"Inicializando la clase {name}")
super().__init__(name, bases, attrs)
__call__
Define el comportamiento al crear instancias de la clase:
class MetaCall(type):
def __call__(cls, *args, **kwargs):
print(f"Creando instancia de {cls.__name__}")
return super().__call__(*args, **kwargs)
Ejemplos prácticos de uso
Conversión automática de atributos
class MetaMayusculas(type):
def __new__(cls, name, bases, attrs):
attrs_mayus = {}
for attr_name, attr_value in attrs.items():
if not attr_name.startswith('__'):
attrs_mayus[attr_name.upper()] = attr_value
else:
attrs_mayus[attr_name] = attr_value
return super().__new__(cls, name, bases, attrs_mayus)
class MiClase(metaclass=MetaMayusculas):
attr1 = 'valor1'
attr2 = 'valor2'
print(hasattr(MiClase, 'ATTR1')) # True
print(hasattr(MiClase, 'attr1')) # False
Implementación del patrón Singleton
class MetaSingleton(type):
_instancias = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instancias:
cls._instancias[cls] = super().__call__(*args, **kwargs)
return cls._instancias[cls]
class ConexionBD(metaclass=MetaSingleton):
def __init__(self):
print("Creando conexión a la base de datos")
db1 = ConexionBD()
db2 = ConexionBD()
print(db1 is db2) # True
Validación de la estructura de clases
class MetaValidado(type):
def __new__(cls, na