types.new_class() crea clases dinámicamente siguiendo las reglas principales de una declaración class. Selecciona la metaclase apropiada, prepara el namespace y ejecuta un callback que añade atributos, métodos y metadatos antes de construir el tipo final.
Es útil en frameworks, ORMs, generadores de modelos, sistemas de plugins y APIs basadas en schemas. Para casos simples, type() puede ser suficiente; new_class es valiosa cuando importan metaclases, __prepare__ o bases dinámicas.
Firma
types.new_class(name, bases=(), kwds=None, exec_body=None)
name define el nombre, bases contiene las clases base, kwds representa opciones del encabezado y exec_body recibe el namespace preparado.
Ejemplo básico
from types import new_class
def completar(namespace):
namespace["categoria"] = "dinámica"
def describir(self):
return self.categoria
namespace["describir"] = describir
Producto = new_class("Producto", (), {}, completar)
print(Producto().describir())
El callback modifica el namespace antes de la creación.
Equivalencia conceptual
class Producto:
categoria = "dinámica"
def describir(self):
return self.categoria
Una declaración normal es preferible cuando la estructura se conoce al escribir el programa. La generación dinámica debe reservarse para definiciones determinadas en runtime.
Usar una clase base
class Modelo:
def guardar(self):
print("guardando", type(self).__name__)
def cuerpo(ns):
ns["tabla"] = "clientes"
Cliente = new_class("Cliente", (Modelo,), exec_body=cuerpo)
La clase generada participa normalmente en herencia, MRO, descriptors y super().
Metaclase explícita
class Meta(type):
def __new__(mcls, nombre, bases, namespace):
namespace["registrada"] = True
return super().__new__(mcls, nombre, bases, namespace)
Generada = new_class(
"Generada",
(),
{"metaclass": Meta},
lambda ns: ns.update(valor=10),
)
El diccionario kwds corresponde a keywords del encabezado de clase. La opción metaclass participa en la creación y no se convierte en un atributo ordinario.
__prepare__ y namespaces especiales
Las metaclases pueden implementar __prepare__ para devolver un mapping especializado. new_class respeta este protocolo, a diferencia de una solución simple que crea un diccionario y llama a type.
types.prepare_class
Para controlar todas las etapas, types.prepare_class() calcula la metaclase y devuelve el namespace preparado. new_class combina esos pasos en una interfaz cómoda.
Bases dinámicas y __mro_entries__
Objetos usados como bases pueden ofrecer __mro_entries__ y ser reemplazados por clases reales. El módulo types también incluye resolve_bases. Este mecanismo aparece en genéricos y frameworks avanzados.
Métodos con closures
def crear_modelo(nombre, campos):
def cuerpo(ns):
ns["__annotations__"] = dict(campos)
def __repr__(self):
valores = ", ".join(
f"{campo}={getattr(self, campo, None)!r}"
for campo in campos
)
return f"{nombre}({valores})"
ns["__repr__"] = __repr__
return new_class(nombre, (), exec_body=cuerpo)
Ten cuidado con la captura de variables de loops al generar varios métodos.
Definir __module__ y __qualname__
Las clases públicas generadas necesitan metadatos coherentes para documentación, errores y serialización.
def cuerpo(ns):
ns["__module__"] = __name__
ns["__doc__"] = "Modelo generado dinámicamente."
Pickle e importabilidad
Pickle normalmente necesita que la clase sea accesible mediante un nombre importable. Una clase creada dentro de una función puede tener que registrarse en el namespace del módulo.
Aplicar decorators
La clase devuelta puede pasarse a dataclasses.dataclass, registradores u otros decorators. Los frameworks que inspeccionan durante la creación pueden requerir atributos dentro de exec_body.
Seguridad
No conviertas schemas no confiables directamente en bases, metaclases, identificadores o código ejecutable. Valida nombres, usa listas permitidas y evita exec cuando una construcción estructurada sea suficiente.
new_class frente a type
type(nombre, bases, namespace) es conciso cuando el namespace ya está disponible. new_class es mejor cuando necesitas reproducir el proceso de definición, incluida la selección de metaclase y el namespace preparado.
Errores comunes
- Generar una clase cuando una dataclass u objeto simple basta.
- Olvidar
__module__y perjudicar pickle o documentación. - Capturar mal variables de loops.
- Confiar en bases o metaclases externas.
- No probar MRO, herencia e introspección.
Buenas prácticas
Centraliza la generación en una factory, valida schemas, usa nombres deterministas, conserva metadatos y prueba las clases como APIs públicas. Consulta las guías internas sobre módulo types, programación orientada a objetos y dataclasses.
Conclusión
types.new_class ofrece una forma estructurada de generar clases en runtime respetando metaclases, namespaces preparados y herencia. Es adecuada para infraestructura basada en definiciones dinámicas.







