josiete.com

Python 3.15: importar solo cuando hace falta

Logo de Python azul y amarillo en relieve sobre una superficie oscura, con bloques desenfocados al fondo

Importar una librería puede implicar leer archivos, ejecutar su código de inicialización y cargar otras dependencias. Ese trabajo se paga aunque después solo queramos mostrar la ayuda de una herramienta. Python 3.15 incorpora Explicit Lazy Imports, definido en el PEP 810 y recogido en las novedades de Python 3.15: podemos declarar un import y aplazar su carga hasta el primer uso.

Un ejemplo pequeño

Este script requiere Python 3.15 o posterior. Utiliza únicamente la biblioteca estándar:

# herramienta.py
import argparse
lazy import zoneinfo

parser = argparse.ArgumentParser()
parser.add_argument("--zona", help="Consultar una zona horaria IANA")
args = parser.parse_args()

if args.zona:
    print(zoneinfo.ZoneInfo(args.zona))  # Aquí se carga zoneinfo
else:
    print("Sin consulta de zona horaria")

Con python herramienta.py --help, la herramienta termina sin cargar zoneinfo, siempre que otra dependencia no lo haya importado antes. Con python herramienta.py --zona Europe/Madrid, el acceso a zoneinfo provoca su carga; esta consulta requiere que el sistema disponga de datos de zonas horarias o del paquete tzdata.

También existe lazy from zoneinfo import ZoneInfo. Se aplaza la carga del módulo completo, no se ejecuta únicamente la clase solicitada. La referencia del lenguaje limita esta sintaxis al nivel de módulo: no se permite dentro de funciones, clases o bloques try, ni con import *.

Dónde se ahorra trabajo

  • Arranque más rápido: se retrasa la inicialización de dependencias hasta que una ruta del programa las necesita.
  • Menos CPU y lecturas innecesarias: una herramienta con una opción de gráficos podría evitar cargar una librería pesada de visualización cuando solo exporta texto.
  • Menor memoria: los módulos que no llegan a cargarse tampoco crean sus objetos ni inicializan sus dependencias exclusivas.

El ahorro depende del programa. Si acabamos utilizando todos los módulos, buena parte del coste simplemente cambia de momento. Conviene comparar procesos nuevos, midiendo el arranque, la primera operación y la memoria con cargas representativas; no hay un porcentaje universal de mejora.

Los costes del aplazamiento

El primer acceso es más lento: la primera operación que necesita el módulo asume su importación. En un servicio puede interesar precargarlo antes de atender peticiones para evitar esa latencia inicial.

Los errores aparecen más tarde: una dependencia ausente o un fallo de inicialización puede pasar inadvertido hasta que se ejecute esa funcionalidad. Hay que probar también las rutas opcionales y capturar los errores alrededor del uso cuando corresponda. Además, los efectos secundarios del import, como registrar plugins, también se retrasan.

El tipado estático necesita herramientas compatibles: según el PEP, un analizador puede resolver estos nombres como en un import normal; el tipo no se vuelve desconocido por ser lazy. La nueva sintaxis sí exige soporte del analizador y del formateador. También hay que distinguir el análisis estático de la ejecución: un framework que evalúa anotaciones puede provocar la carga del módulo. lazy import y los imports protegidos con TYPE_CHECKING tienen comportamientos distintos en tiempo de ejecución.

Para mantener compatibilidad con versiones anteriores, puede usarse __lazy_modules__ = ["zoneinfo"] seguido de import zoneinfo: desde 3.15 será lazy y en versiones anteriores seguirá siendo inmediato. La sintaxis con lazy produce un error de sintaxis en Python 3.14 y anteriores.