Python 3.15 ya está aquí: lazy imports, frozendict y un profiler de 1 MHz
Python 3.15.0 salió el 9 de octubre de 2026. Repasamos sus novedades (lazy imports, frozendict, sentinel, Tachyon, UTF-8 por defecto, desempaquetado en comprehensions) y qué cambia para un equipo que tiene Python en producción.
El 9 de octubre de 2026 se publicó Python 3.15.0. Hugo van Kemenade, release manager de esta versión, ha contado en su blog cómo fue el día del lanzamiento: certificados caducados que tumbaron docs.python.org, un test intermitente en los buildbots, tres correcciones de última hora en lazy imports que obligaron a sacar una tercera release candidate "sorpresa" el 3 de octubre, unas dos horas solo para construir el instalador de Windows y la firma de todos los artefactos con Sigstore antes de subirlos a python.org.
El resultado merece la pena. Es una de las versiones con más cambios visibles para el día a día de los últimos años, y varias novedades tocan directamente a quien tiene Python en producción: CLIs, APIs, pipelines de datos o agentes de IA.
Las novedades de un vistazo
PEP 810
Lazy imports explícitos
El módulo se carga la primera vez que se usa, no al arrancar.
PEP 814
frozendict nativo
Un diccionario inmutable y hashable, sin librerías externas.
PEP 661
sentinel nativo
Valores centinela únicos con una representación legible.
PEP 799
Paquete profiling y Tachyon
Profiler estadístico de hasta 1.000.000 muestras por segundo.
PEP 798
Desempaquetado en comprehensions
* y ** directamente dentro de listas, sets, dicts y generadores.
PEP 686
UTF-8 por defecto
La E/S usa UTF-8 sea cual sea el locale del sistema.
PEP 831
Frame pointers activados
Pilas completas para profilers y depuradores del sistema.
PEP 803
ABI estable para free-threading
Extensiones en C compatibles con builds sin GIL (abi3t).
Lazy imports: arrancar sin cargar lo que no usas
Es la novedad estrella. Con la nueva palabra clave lazy, un import ya no ejecuta el módulo al arrancar: lo carga la primera vez que se accede a él. Para una CLI con muchos subcomandos, una función serverless o un worker que solo usa una parte de sus dependencias en cada petición, el ahorro en tiempo de arranque puede ser muy notable.
lazy import json
lazy from pathlib import Path
print("Arrancando...") # json y pathlib aún no se han cargado
data = json.loads('{"clave": "valor"}') # json se carga aquíTambién se puede activar de forma global con -X lazy_imports o la variable PYTHON_LAZY_IMPORTS, y las librerías que necesiten seguir funcionando en 3.14 pueden declarar __lazy_modules__ a nivel de módulo.
frozendict y sentinel, por fin en builtins
frozendict es un mapping inmutable y hashable que conserva el orden de inserción. No es una subclase de dict, y ya funciona con copy, json,pickle y pprint. Es ideal para configuración que no debe cambiar en tiempo de ejecución o para usar diccionarios como clave de caché. sentinel resuelve el clásicoMISSING = object() con valores únicos, legibles en los logs, que se conservan al copiarlos y se pueden serializar.
config = frozendict(entorno="prod", region="eu-west-1")
config["region"] = "us-east-1" # TypeError: no admite asignación
cache = {config: "resultado"} # es hashable: sirve como clave
MISSING = sentinel("MISSING") # centinela propio, sin object()Desempaquetado dentro de las comprehensions
Aplanar una lista de listas o fusionar varios diccionarios ya no necesitaitertools.chain ni bucles anidados. Los operadores* y ** funcionan dentro de comprehensions de listas, sets y diccionarios, y también en generadores, incluidos los asíncronos.
listas = [[1, 2], [3, 4], [5]]
[*l for l in listas] # [1, 2, 3, 4, 5]
dicts = [{"a": 1}, {"b": 2}, {"a": 3}]
{**d for d in dicts} # {'a': 3, 'b': 2}Tachyon: profiling en producción sin tocar el código
Python 3.15 agrupa sus herramientas en un nuevo paquete profiling(PEP 799): profiling.tracing recoge el antiguo cProfile yprofiling.sampling estrena Tachyon, un profiler estadístico que muestrea hasta 1.000.000 de veces por segundo y se puede enganchar a un proceso que ya está corriendo. Mide tiempo real, CPU, retención del GIL o excepciones, entiende código asíncrono y exporta flame graphs interactivos, heatmaps por línea, el formato de Firefox Profiler o una vista en vivo en la terminal.
python -m profiling.sampling run script.py
python -m profiling.sampling attach <pid>
python -m profiling.sampling run --mode cpu --flamegraph perfil.html script.pyLo complementa la activación por defecto de los frame pointers (PEP 831): herramientas del sistema como perf ya pueden reconstruir la pila completa de llamadas sin recompilar CPython.
UTF-8 por defecto y free-threading más maduro
Con la PEP 686, abrir un archivo sin indicar encoding usa UTF-8 en todos los sistemas operativos. Desaparece una fuente histórica de errores con tildes y eñes en Windows, aunque es también el cambio que más puede romper código antiguo (ver la lista de abajo).
En paralelo, el Python sin GIL sigue avanzando. La PEP 803 crea un ABI estable para los builds free-threaded (abi3t), de modo que una extensión en C puede publicar un único wheel compatible con varias versiones, y meson-python, scikit-build-core y Maturin ya lo soportan. Además, el instalador oficial de macOS incluye free-threading por defecto.
Mensajes de error que piensan como tú
Los AttributeError sugieren ahora el método equivalente cuando escribes con hábitos de otro lenguaje, e incluso proponen el atributo correcto cuando está anidado dentro de otro objeto. Un detalle pequeño que ahorra búsquedas, sobre todo a quien llega a Python desde JavaScript o Java.
[1, 2, 3].push(4)
# AttributeError: 'list' object has no attribute 'push'. Did you mean '.append'?
'hola'.toUpperCase()
# AttributeError: 'str' object has no attribute 'toUpperCase'. Did you mean '.upper'?Otros cambios que conviene conocer
Más color en la terminal
python --help, argparse, sqlite3, timeit, difflib y otras CLI de la biblioteca estándar muestran salida coloreada cuando detectan una TTY.
JIT experimental mejorado
El compilador JIT sigue siendo experimental, pero la documentación oficial lo describe como significativamente mejorado respecto a 3.14.
Tipado
TypeForm para anotar formas de tipo (PEP 747), TypedDict con extra items tipados (PEP 728) y bases disjuntas (PEP 800).
Archivos .start
Los paquetes declaran su código de arranque como pkg.mod:callable (PEP 829), en lugar de las líneas import de los .pth, que quedan obsoletas.
bytearray.take_bytes()
Devuelve bytes y vacía el buffer sin copiar memoria.
Binarios oficiales
En macOS el instalador incluye free-threading por defecto y en Windows de 64 bits se usa el intérprete con tail calls.
Antes de migrar tus proyectos
Prueba tu suite con UTF-8 forzado
Si algún proceso leía o escribía archivos confiando en el locale de Windows (cp1252), ahora el resultado cambia. Puedes volver al comportamiento anterior con PYTHONUTF8=0 mientras lo corriges.
Busca imports del módulo profile
Queda obsoleto y se eliminará en 3.17. El sustituto es profiling.tracing (el antiguo cProfile) o profiling.sampling.
Revisa __cached__ y las APIs eliminadas
El atributo __cached__ de los módulos desaparece (usa __spec__.cached), y se retiran APIs ya obsoletas de pathlib, typing, ast, importlib o threading, entre otros.
Espera a tus dependencias críticas
El propio equipo de release esperó a que Pydantic y aiohttp publicaran versiones compatibles. Comprueba que tus librerías con extensiones en C ya tienen wheels para 3.15.
Mide antes de activar lazy imports
Lánzalo primero con -X lazy_imports o PYTHON_LAZY_IMPORTS en un entorno de pruebas y compara el tiempo de arranque con Tachyon antes de reescribir imports.
Para saber más
- What's New In Python 3.15, la documentación oficial con todos los cambios.
- Releasing Python 3.15.0, la crónica del lanzamiento de Hugo van Kemenade.
- Python 3.15 ya salió: 5 novedades que vas a usar, un resumen en vídeo de Code Artisans.
¿Quieres aplicar esto en tu equipo?
En Kuuli Technologies llevamos estas metodologías a equipos reales. Escríbenos si quieres una sesión de trabajo o una consultoría sobre agentes IA y SDD.