Los scripts que tardan varios segundos o minutos deben informar que el trabajo continúa. Sin una señal visible, una descarga, conversión de archivos o limpieza de datos puede parecer bloqueada aunque funcione correctamente. La biblioteca tqdm en Python añade barras de progreso a los bucles con muy poco código.
En esta guía aprenderás a instalar tqdm, envolver iterables, mostrar descripciones y unidades, actualizar el progreso manualmente, trabajar con archivos, pandas, descargas, barras anidadas y ejecuciones no interactivas.
Qué es tqdm
tqdm recibe un iterable y muestra elementos completados, total, tiempo transcurrido, velocidad y tiempo estimado restante cuando conoce el tamaño total.
Consulta la documentación oficial de tqdm y su página en PyPI para revisar opciones y versiones.
Instalar tqdm
python -m pip install tqdmComprueba la instalación:
python -c "import tqdm; print(tqdm.__version__)"Primera barra de progreso
import time
from tqdm import tqdm
for elemento in tqdm(range(100)):
time.sleep(0.03)sleep() solamente simula trabajo. En un programa real, el bucle podría procesar imágenes, leer archivos o enviar solicitudes.
Añadir una descripción
for elemento in tqdm(range(50), desc="Procesando registros"):
time.sleep(0.04)Una descripción breve explica qué representa la barra.
Mostrar unidades útiles
archivos = ["a.csv", "b.csv", "c.csv"]
for nombre in tqdm(archivos, desc="Convirtiendo", unit="archivo"):
time.sleep(0.4)Para bytes, combina unit="B" con unit_scale=True:
total_bytes = 10_000_000
with tqdm(
total=total_bytes,
unit="B",
unit_scale=True,
unit_divisor=1024,
desc="Escribiendo",
) as progreso:
for tamano in [2_000_000] * 5:
time.sleep(0.2)
progreso.update(tamano)Listas, generadores e iterables
Una lista normalmente proporciona su longitud automáticamente.
nombres = ["Ana", "Luis", "Marta", "Diego"]
for nombre in tqdm(nombres, desc="Saludando"):
print(nombre)Un generador puede no informar el total. Pásalo manualmente cuando lo conozcas:
def cuadrados(limite):
for numero in range(limite):
yield numero * numero
for valor in tqdm(cuadrados(1000), total=1000, desc="Calculando"):
passActualizar el progreso manualmente
Cuando el proceso no es un bucle sencillo, crea una barra con total y llama a update().
pasos = ["leer", "validar", "transformar", "guardar"]
with tqdm(total=len(pasos), desc="Proceso", unit="paso") as progreso:
for paso in pasos:
time.sleep(0.5)
progreso.set_postfix(actual=paso)
progreso.update(1)Actualiza solamente después de completar el trabajo real correspondiente.
Mostrar métricas cambiantes
errores = 0
with tqdm(range(100), desc="Validando") as progreso:
for numero in progreso:
if numero % 17 == 0:
errores += 1
progreso.set_postfix(errores=errores)En bucles extremadamente rápidos, actualizar texto en cada iteración puede ser innecesario.
Escribir mensajes sin romper la barra
from tqdm import tqdm
for numero in tqdm(range(20), desc="Revisando"):
if numero == 7:
tqdm.write("Caso especial encontrado en 7")Para registros permanentes utiliza logging. La barra es una interfaz temporal.
Controlar la frecuencia de actualización
mininterval: tiempo mínimo entre actualizaciones.miniters: iteraciones mínimas entre refrescos.disable: desactiva la salida.leave: conserva o elimina la barra completada.dynamic_ncols: adapta el ancho a la terminal.
for elemento in tqdm(
range(1_000_000),
mininterval=0.5,
dynamic_ncols=True,
leave=False,
):
resultado = elemento * 2Desactivar la barra fuera de una terminal
import sys
from tqdm import tqdm
for elemento in tqdm(
range(100),
disable=not sys.stderr.isatty(),
):
passEsto evita caracteres de animación en archivos de log o sistemas de integración continua.
Barras anidadas
import time
from tqdm import tqdm
for lote in tqdm(range(3), desc="Lotes", position=0):
for elemento in tqdm(
range(20),
desc=f"Lote {lote + 1}",
position=1,
leave=False,
):
time.sleep(0.02)Evita demasiados niveles porque la terminal puede volverse difícil de leer.
Procesar archivos con pathlib
from pathlib import Path
from tqdm import tqdm
carpeta = Path("documentos")
archivos = list(carpeta.glob("*.txt"))
for ruta in tqdm(archivos, desc="Leyendo", unit="archivo"):
contenido = ruta.read_text(encoding="utf-8")
palabras = len(contenido.split())
print(ruta.name, palabras)Convertir el resultado a lista permite conocer el total. En directorios enormes, valora si merece la pena cargar todas las rutas.
Usar tqdm con pandas
import pandas as pd
from tqdm.auto import tqdm
tqdm.pandas(desc="Normalizando")
datos = pd.DataFrame({"nombre": [" Ana ", "LUIS", " marta"]})
datos["limpio"] = datos["nombre"].progress_apply(
lambda valor: valor.strip().title()
)
print(datos)Las operaciones vectorizadas de pandas suelen ser más rápidas que apply(). La barra mejora la visibilidad, no el rendimiento.
tqdm.auto en terminal y notebooks
from tqdm.auto import tqdm
for elemento in tqdm(range(100)):
passtqdm.auto selecciona una presentación adecuada en muchos entornos.
Descargar un archivo con progreso
from pathlib import Path
from urllib.request import urlopen
from tqdm import tqdm
def descargar(url, destino, tamano_bloque=64 * 1024):
destino = Path(destino)
with urlopen(url, timeout=30) as respuesta:
total = int(respuesta.headers.get("Content-Length", 0))
with destino.open("wb") as archivo, tqdm(
total=total or None,
unit="B",
unit_scale=True,
unit_divisor=1024,
desc=destino.name,
) as progreso:
while bloque := respuesta.read(tamano_bloque):
archivo.write(bloque)
progreso.update(len(bloque))Si el servidor no envía Content-Length, la barra puede mostrar bytes y velocidad sin porcentaje.
Proyecto: contador de líneas con barra
import argparse
import sys
from pathlib import Path
from tqdm import tqdm
def contar_lineas(ruta):
with ruta.open("r", encoding="utf-8", errors="replace") as archivo:
return sum(1 for _ in archivo)
def buscar_txt(carpeta, recursivo=False):
patron = "**/*.txt" if recursivo else "*.txt"
return sorted(carpeta.glob(patron))
def main():
parser = argparse.ArgumentParser()
parser.add_argument("carpeta", type=Path)
parser.add_argument("--recursivo", action="store_true")
parser.add_argument("--sin-progreso", action="store_true")
args = parser.parse_args()
if not args.carpeta.is_dir():
raise SystemExit(f"No es una carpeta: {args.carpeta}")
archivos = buscar_txt(args.carpeta, args.recursivo)
ocultar = args.sin_progreso or not sys.stderr.isatty()
total_lineas = 0
for ruta in tqdm(
archivos,
desc="Contando",
unit="archivo",
disable=ocultar,
):
total_lineas += contar_lineas(ruta)
print(f"Archivos: {len(archivos)}")
print(f"Líneas: {total_lineas}")
if __name__ == "__main__":
main()Errores frecuentes
- No proporcionar
totalcuando el iterable no expone longitud. - Llamar a
update()con el total final en vez del incremento. - Usar
print()repetidamente mientras la barra está activa. - Crear una barra nueva en cada iteración.
- Convertir un generador enorme en lista solamente para calcular su tamaño.
- Mantener animaciones en logs no interactivos.
- Creer que tqdm acelera el procesamiento.
Conclusión
tqdm mejora la experiencia de scripts largos sin cambiar su lógica principal. Envuelve iterables conocidos, utiliza actualizaciones manuales para trabajo por bloques, muestra unidades claras y desactiva la animación cuando la salida no sea interactiva.
Una buena barra representa trabajo realmente completado y permanece separada de los registros permanentes de la aplicación.
Lecturas relacionadas
- Para gestionar las rutas del ejemplo, consulta la guía de pathlib en Python.
- Aprende a conservar registros permanentes con el módulo logging de Python.
- Convierte el proyecto final en una herramienta configurable con la guía de argparse para crear una CLI en Python.

