Funciones, métodos, módulos, generators, frames, code objects y aliases genéricos son objetos reales en el runtime de Python. El módulo types en Python proporciona nombres estándar para muchas de estas estructuras y utilidades para crear clases dinámicas, preparar namespaces, adaptar generators a coroutines y exponer mappings de solo lectura.
Esta guía explica SimpleNamespace, MappingProxyType, MethodType, new_class(), prepare_class(), resolve_bases() y los tipos del runtime expuestos por el módulo. Complementa nuestros artículos sobre inspect en Python, bytecode con dis, symtable, contextvars y copyreg.
Por qué existe el módulo types
Muchos tipos internos pueden descubrirse con type(), pero expresiones como type(lambda: None) son poco claras. El módulo ofrece nombres explícitos como FunctionType, GeneratorType, CoroutineType y TracebackType.
import types
print(isinstance(lambda: None, types.FunctionType))
print(isinstance((x for x in range(3)), types.GeneratorType))Estos nombres son útiles en introspección, depuradores, frameworks y validaciones de bajo nivel. La lógica de negocio debería preferir protocolos y collections.abc cuando importa el comportamiento más que el tipo exacto.
SimpleNamespace
SimpleNamespace crea un objeto sencillo cuyos atributos se almacenan en un diccionario interno.
from types import SimpleNamespace
config = SimpleNamespace(
host="localhost",
puerto=8000,
debug=True,
)
print(config.host)
config.puerto = 9000Es útil para resultados pequeños, configuración temporal, mocks y agrupación de valores. Su representación muestra atributos y la igualdad compara el contenido del namespace.
SimpleNamespace no sustituye un modelo de dominio
El objeto no valida campos, no impone tipos y acepta atributos nuevos libremente.
config.puertoo = 7000 # typo aceptadoCuando importan invariantes, documentación, métodos o validación, usa dataclass, NamedTuple, TypedDict o una clase ordinaria.
MappingProxyType
MappingProxyType crea una vista dinámica de solo lectura de un mapping.
from types import MappingProxyType
origen = {"modo": "producción", "intentos": 3}
publico = MappingProxyType(origen)
print(publico["modo"])
# publico["modo"] = "prueba" # TypeErrorEl proxy impide cambios mediante la referencia pública, pero refleja actualizaciones realizadas sobre el mapping original.
origen["intentos"] = 5
print(publico["intentos"])Ofrece acceso de solo lectura, no inmutabilidad profunda.
Cuándo usar MappingProxyType
Es útil para exponer registries, metadatos, configuración interna y tablas de dispatch sin entregar una referencia mutable.
_handlers = {"json": procesar_json}
handlers = MappingProxyType(_handlers)Los valores anidados todavía pueden ser mutables. Copia o congela las estructuras internas cuando sea necesario.
MethodType
MethodType vincula una función a una instancia y produce un método ligado.
from types import MethodType
class Usuario:
def __init__(self, nombre):
self.nombre = nombre
def saludar(self):
return f"Hola, {self.nombre}"
usuario = Usuario("Ana")
usuario.saludar = MethodType(saludar, usuario)
print(usuario.saludar())Este recurso aparece en tests, instrumentación y plugins. Para comportamiento permanente, composición o subclasses suelen ser más claras.
FunctionType y LambdaType
FunctionType representa funciones definidas en Python. LambdaType es un alias del mismo tipo.
import types
def sumar(a, b):
return a + b
assert isinstance(sumar, types.FunctionType)
assert types.LambdaType is types.FunctionTypeBuilt-ins como len usan BuiltinFunctionType, y métodos built-in pueden usar BuiltinMethodType.
GeneratorType, CoroutineType y AsyncGeneratorType
import types
def numeros():
yield 1
async def buscar():
return 42
async def eventos():
yield "inicio"
generator = numeros()
coroutine = buscar()
async_generator = eventos()
print(isinstance(generator, types.GeneratorType))
print(isinstance(coroutine, types.CoroutineType))
print(isinstance(async_generator, types.AsyncGeneratorType))Las coroutines creadas deben esperarse o cerrarse para evitar warnings.
types.coroutine()
types.coroutine() transforma una función generator en una coroutine compatible con await. Su uso principal es la interoperabilidad de bajo nivel entre generators y sistemas async.
import types
@types.coroutine
def esperar_evento():
resultado = yield "evento"
return resultadoLas aplicaciones modernas normalmente deberían usar async def. El decorator sigue siendo útil para runtimes y adapters legados.
ModuleType
ModuleType crea objetos de módulo.
from types import ModuleType
modulo = ModuleType("mi_modulo", "Módulo creado dinámicamente")
modulo.valor = 42
print(modulo.__name__)
print(modulo.valor)Para módulos que participan en imports, la documentación oficial de importlib recomienda specs y importlib.util.module_from_spec(), que inicializa más atributos correctamente.
Crear clases con new_class()
new_class() implementa el protocolo moderno de creación dinámica.
import types
def llenar(namespace):
namespace["categoria"] = "dinámica"
def describir(self):
return self.categoria
namespace["describir"] = describir
Dinamica = types.new_class(
"Dinamica",
bases=(object,),
exec_body=llenar,
)
print(Dinamica().describir())La función respeta metaclasses, __prepare__() y resolución de bases.
Parámetros de new_class()
El primer argumento es el nombre; bases contiene las bases; kwds puede incluir metaclass; y exec_body recibe el namespace preparado.
Plugin = types.new_class(
"Plugin",
bases=(BasePlugin,),
kwds={"metaclass": MetaPlugin},
exec_body=lambda ns: ns.update({"version": 1}),
)Valida nombres, bases y metaclasses que provienen de configuración. Crear una clase dinámica no es una sandbox.
prepare_class()
prepare_class() calcula la metaclass y prepara el namespace antes de construir la clase.
meta, namespace, palabras = types.prepare_class(
"MiClase",
(Base,),
{"metaclass": MiMeta},
)
namespace["atributo"] = 10
MiClase = meta("MiClase", (Base,), namespace, **palabras)Los frameworks pueden inspeccionar o llenar el namespace entre preparación y construcción.
resolve_bases()
Las bases pueden incluir objetos con __mro_entries__() en lugar de tipos reales. resolve_bases() aplica ese protocolo.
resueltas = types.resolve_bases(bases_originales)Esto importa para aliases genéricos y abstracciones utilizadas en la lista de bases.
get_original_bases()
get_original_bases() recupera las bases declaradas antes de la resolución cuando existe __orig_bases__.
from typing import Generic, TypeVar
T = TypeVar("T")
class Caja(Generic[T]):
pass
class CajaTexto(Caja[str]):
pass
print(types.get_original_bases(CajaTexto))Los frameworks de typing pueden recuperar parámetros genéricos. La ausencia de metadatos debe tratarse como normal.
DynamicClassAttribute
DynamicClassAttribute crea un descriptor similar a property, pero permite manejo diferente al acceder desde la clase.
Lo utiliza, por ejemplo, enum. El código de aplicación normalmente debería preferir property.
CodeType
CodeType representa objetos de código compilado.
codigo = compile("x = 1", "<ejemplo>", "exec")
print(isinstance(codigo, types.CodeType))El constructor cambia entre versiones y expone detalles internos. Prefiere compile() y code.replace().
nuevo = codigo.replace(co_filename="archivo_virtual.py")Ejecutar un code object sigue siendo ejecutar código.
FrameType y TracebackType
FrameType representa frames y TracebackType la cadena de traceback.
try:
1 / 0
except ZeroDivisionError as error:
tb = error.__traceback__
print(isinstance(tb, types.TracebackType))
print(isinstance(tb.tb_frame, types.FrameType))Los frames retienen variables locales y pueden prolongar la vida de objetos. Libera referencias después del diagnóstico.
GenericAlias y UnionType
GenericAlias representa expresiones como list[int]. UnionType representa unions creadas con |.
alias = list[int]
union = int | str
print(isinstance(alias, types.GenericAlias))
print(isinstance(union, types.UnionType))Estos objetos apoyan introspección de annotations, pero no validan valores en runtime.
Tipos singleton
El módulo ofrece nombres como NoneType, EllipsisType y NotImplementedType.
print(isinstance(None, types.NoneType))
print(isinstance(Ellipsis, types.EllipsisType))
print(isinstance(NotImplemented, types.NotImplementedType))Esto evita expresiones como type(None).
Descriptors de extensiones
GetSetDescriptorType y MemberDescriptorType representan descriptors creados frecuentemente por extensiones C y __slots__.
class Ejemplo:
__slots__ = ("valor",)
print(isinstance(Ejemplo.valor, types.MemberDescriptorType))Algunos runtimes pueden usar el mismo tipo interno para ambas categorías. Prueba las implementaciones soportadas.
Seguridad e introspección
El módulo expone estructuras poderosas del runtime. Crear clases, módulos, métodos, metaclasses o code objects no valida su origen.
No ejecutes funciones, bases, metaclasses o bytecode proporcionados por usuarios. Los plugins necesitan interfaces, autorización y aislamiento.
Errores frecuentes
- Usar
SimpleNamespacepara datos que necesitan validación. - Tratar
MappingProxyTypecomo inmutabilidad profunda. - Añadir métodos dinámicos sin documentar.
- Construir
CodeTypemanualmente. - Aceptar bases o metaclasses no confiables.
- Suponer que annotations validan valores.
- Conservar frames indefinidamente.
- Usar tipos exactos cuando un protocolo es mejor.
Buenas prácticas
- Usa nombres de types para introspección clara.
- Prefiere dataclasses para modelos estructurados.
- Expón proxies de solo lectura.
- Crea clases dinámicas con
new_class(). - Usa
module_from_spec()para imports. - Prefiere
async def. - Libera frames después del análisis.
- Prueba compatibilidad entre versiones.
Conclusión
El módulo types en Python da nombres explícitos a estructuras del runtime y utilidades para namespaces, mappings de solo lectura, métodos ligados y clases dinámicas. Es especialmente útil en frameworks, depuradores e introspección.
Estas APIs operan cerca de los mecanismos internos. Prefiriendo abstracciones simples, validando plugins y metaclasses y evitando constructores inestables, puedes aprovechar la flexibilidad del runtime sin volver frágil la aplicación.





