Path.walk en Python: recorre directorios

Publicado el: 28/08/2026
Tempo de leitura: 5 minutos
Picturesque wooden boardwalk leading to a serene beach under clear blue skies.

Recorrer árboles de directorios es habitual en automatización, backups, auditoría, limpieza e indexación. Durante años, os.walk() fue la herramienta estándar. Python moderno también ofrece pathlib.Path.walk(), que integra el recorrido con la API orientada a objetos de pathlib y devuelve el directorio actual como un objeto Path.

Esta guía cubre recorrido top-down y bottom-up, poda de carpetas, manejo de errores, enlaces simbólicos, tamaños, eliminación segura, orden determinista, cambios concurrentes y compatibilidad con versiones anteriores.

Primer ejemplo con Path.walk

from pathlib import Path

raiz = Path("proyecto")

for directorio, subdirs, archivos in raiz.walk():
    print("Directorio:", directorio)
    for nombre in archivos:
        ruta = directorio / nombre
        print("  Archivo:", ruta)

Cada iteración devuelve el directorio actual como Path, una lista de nombres de subdirectorios y otra de nombres de archivos. Combina los nombres con el directorio para construir rutas completas.

Por qué usar pathlib

Path reúne unión de rutas, extensiones, lectura, escritura, metadatos y rutas relativas en una API consistente.

for directorio, _, archivos in raiz.walk():
    for nombre in archivos:
        ruta = directorio / nombre
        if ruta.suffix == ".py":
            print(ruta.relative_to(raiz))

Esto evita mezclar repetidamente os.path.join(), os.path.splitext() y otras funciones. La guía de pathlib en Python explica el resto de la API.

Recorrido top-down

Por defecto, el directorio padre se entrega antes que sus hijos.

for directorio, subdirs, archivos in raiz.walk(top_down=True):
    print(directorio)

Este modo permite modificar la lista subdirs para decidir qué carpetas se visitarán.

Ignorar directorios

Elimina nombres de la lista de subdirectorios en el propio objeto.

IGNORAR = {".git", ".venv", "node_modules", "__pycache__"}

for directorio, subdirs, archivos in raiz.walk():
    subdirs[:] = [nombre for nombre in subdirs if nombre not in IGNORAR]

    for nombre in archivos:
        print(directorio / nombre)

La asignación por slice modifica la lista usada internamente por el walker. Escribir subdirs = [...] solo cambia una variable local y no poda la recursión.

Orden determinista

El orden del sistema de archivos no debe considerarse estable. Ordena las listas para resultados reproducibles.

for directorio, subdirs, archivos in raiz.walk():
    subdirs.sort()
    archivos.sort()
    for nombre in archivos:
        print(directorio / nombre)

Ordenar añade trabajo, pero resulta útil en tests, informes y generación determinista.

Recorrido bottom-up

Con top_down=False, los hijos aparecen antes que el padre.

for directorio, subdirs, archivos in raiz.walk(top_down=False):
    print(directorio)

Es apropiado para eliminar árboles porque los archivos y carpetas internas deben borrarse antes que el directorio padre.

Eliminar un árbol con seguridad

from pathlib import Path


def eliminar_arbol(raiz: Path) -> None:
    for directorio, subdirs, archivos in raiz.walk(top_down=False):
        for nombre in archivos:
            (directorio / nombre).unlink()
        for nombre in subdirs:
            (directorio / nombre).rmdir()
    raiz.rmdir()

El ejemplo es destructivo. Valida la raíz, rechaza ubicaciones peligrosas y considera shutil.rmtree() para una implementación consolidada. No elimines rutas formadas desde entrada no confiable sin límites estrictos.

Manejo de errores con on_error

Los errores al listar un directorio pueden enviarse a una función.

import logging

logger = logging.getLogger(__name__)


def registrar_error(error: OSError) -> None:
    logger.warning("No se pudo acceder a %s: %s", error.filename, error)

for directorio, subdirs, archivos in raiz.walk(on_error=registrar_error):
    ...

Cuando la completitud importa, registra, cuenta o propaga los fallos en vez de omitir áreas inaccesibles sin explicación.

Fallar en el primer error

def fallar(error: OSError) -> None:
    raise error

for elemento in raiz.walk(on_error=fallar):
    ...

Un indexador puede continuar con warnings, mientras una auditoría quizá deba fallar si no puede inspeccionar una zona.

Enlaces simbólicos

Los symlinks a directorios requieren cuidado. Seguirlos puede crear ciclos o salir del árbol esperado.

for directorio, subdirs, archivos in raiz.walk(follow_symlinks=False):
    ...

No seguir enlaces es el valor más seguro. Si debes seguirlos, registra directorios reales visitados e impone límites.

Detectar ciclos

En sistemas compatibles, un conjunto de pares dispositivo e inode puede detectar repeticiones.

visitados: set[tuple[int, int]] = set()

for directorio, subdirs, archivos in raiz.walk(follow_symlinks=True):
    info = directorio.stat()
    clave = (info.st_dev, info.st_ino)
    if clave in visitados:
        subdirs.clear()
        continue
    visitados.add(clave)

La semántica de inode y enlaces cambia entre plataformas, sistemas de red y puntos de montaje. Prueba en el entorno real.

Buscar archivos por extensión

EXTENSIONES = {".py", ".toml", ".json"}

encontrados: list[Path] = []
for directorio, subdirs, archivos in raiz.walk():
    subdirs[:] = [d for d in subdirs if d not in {".git", ".venv"}]
    for nombre in archivos:
        ruta = directorio / nombre
        if ruta.suffix.lower() in EXTENSIONES:
            encontrados.append(ruta)

Para un patrón recursivo simple sin poda, Path.rglob() puede ser más corto. walk() es mejor cuando importan directorios, errores y orden.

Calcular el tamaño del árbol

def tamano_total(raiz: Path) -> int:
    total = 0
    for directorio, _, archivos in raiz.walk():
        for nombre in archivos:
            ruta = directorio / nombre
            try:
                total += ruta.stat().st_size
            except OSError:
                continue
    return total

El resultado no es un snapshot. Los archivos pueden cambiar, aparecer o desaparecer durante el recorrido.

Generar archivos grandes

def mayores_que(raiz: Path, limite: int):
    for directorio, _, archivos in raiz.walk():
        for nombre in archivos:
            ruta = directorio / nombre
            try:
                tamano = ruta.stat().st_size
            except OSError:
                continue
            if tamano > limite:
                yield ruta, tamano

Un generador evita mantener todos los resultados en memoria.

Path.walk frente a os.walk

Los conceptos son parecidos: top-down, poda, callbacks de error y enlaces. La diferencia práctica es el tipo del directorio actual y la integración con pathlib.

import os

for directorio, subdirs, archivos in os.walk("proyecto"):
    ...

Los proyectos que ya usan Path pueden preferir Path.walk. Las bibliotecas compatibles con versiones antiguas pueden seguir con os.walk().

Compatibilidad de versión

Path.walk() se añadió en Python moderno. Un paquete que soporte versiones anteriores puede ofrecer un fallback.

from pathlib import Path
import os


def recorrer_compatible(raiz: Path):
    if hasattr(raiz, "walk"):
        yield from raiz.walk()
        return

    for directorio, subdirs, archivos in os.walk(raiz):
        yield Path(directorio), subdirs, archivos

Declara la versión mínima en pyproject.toml y en los metadatos del paquete.

Cambios concurrentes del sistema de archivos

Los árboles cambian mientras se inspeccionan. Un archivo listado puede desaparecer antes de stat(); los permisos pueden cambiar y una carpeta puede renombrarse. Captura OSError cerca de la operación que puede fallar.

Comprobar exists() no elimina la carrera. Otro proceso puede modificar la ruta entre la comprobación y el uso.

Rutas no confiables y seguridad

Resuelve y valida una raíz proporcionada por usuario contra una base permitida. Los symlinks y segmentos .. pueden escapar del área prevista.

base = Path("/srv/uploads").resolve()
objetivo = (base / entrada_usuario).resolve()

if not objetivo.is_relative_to(base):
    raise ValueError("ruta fuera del área permitida")

La política también debe considerar enlaces simbólicos y reemplazos concurrentes.

Errores comunes

  • Reasignar subdirs en vez de modificar la lista: usa asignación por slice.
  • Confiar en el orden del filesystem: ordena cuando importe el determinismo.
  • Seguir symlinks sin detectar ciclos: el recorrido puede no terminar.
  • Ignorar errores sin política: la completitud puede ser engañosa.
  • Eliminar top-down: usa bottom-up.
  • Suponer un snapshot consistente: el árbol cambia durante el proceso.

Ejemplo completo: inventario de proyecto

from dataclasses import dataclass
from pathlib import Path

@dataclass
class ArchivoInfo:
    ruta: Path
    tamano: int


def inventariar(raiz: Path) -> list[ArchivoInfo]:
    resultado: list[ArchivoInfo] = []
    ignorar = {".git", ".venv", "node_modules", "__pycache__"}

    for directorio, subdirs, archivos in raiz.walk():
        subdirs[:] = sorted(d for d in subdirs if d not in ignorar)
        for nombre in sorted(archivos):
            ruta = directorio / nombre
            try:
                info = ruta.stat()
            except OSError:
                continue
            resultado.append(
                ArchivoInfo(
                    ruta=ruta.relative_to(raiz),
                    tamano=info.st_size,
                )
            )

    return resultado

El inventario ignora carpetas pesadas, produce orden estable, guarda rutas relativas y tolera archivos que desaparecen.

Conclusión

Path.walk() lleva el recorrido de árboles a pathlib. Permite poda, orden, errores, travesía bottom-up y operaciones basadas en Path.

La documentación oficial de Path.walk en Python explica parámetros y enlaces. Úsalo cuando necesites control fino, maneja cambios concurrentes y conserva un fallback si soportas versiones antiguas.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    A man in a blue shirt holding a wall clock above his head, contemplating time.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    asyncio.timeout en Python: controla plazos

    Aprende asyncio.timeout en Python para deadlines, timeout_at, reagendamiento, TaskGroup, cleanup, retries y cancelación asíncrona segura.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    Detailed view of a resting reticulated python showcasing its textured scales and intricate patterns.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    TaskGroup en Python: concurrencia estructurada

    Aprende asyncio.TaskGroup en Python para concurrencia estructurada, resultados, cancelación, ExceptionGroup, timeouts y grupos anidados.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    A monochrome image of a lens on an open dictionary page, highlighting words.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    MappingProxyType: diccionario solo lectura

    Aprende MappingProxyType en Python para exponer diccionarios de solo lectura, crear vistas dinámicas y snapshots, y proteger invariantes sin copias.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    Retro fuel pump with rusty metal and vintage design, featuring a nozzle and liter meter.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Literal en Python: restringe valores

    Aprende typing.Literal en Python para restringir valores, crear overloads, discriminar TypedDict, usar match/case y mejorar APIs tipadas.

    Ler mais

    Tempo de leitura: 4 minutos
    28/08/2026
    Close-up of hands typing on a laptop keyboard, Python book in sight, coding in progress.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    TypedDict en Python: diccionarios tipados

    Aprende TypedDict en Python para diccionarios tipados, claves opcionales, NotRequired, Required, payloads de APIs y variantes discriminadas.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Protocol en Python: tipado estructural

    Aprende Python Protocol para tipado estructural, contratos genéricos, callbacks, runtime_checkable, pruebas e inyección de dependencias.

    Ler mais

    Tempo de leitura: 4 minutos
    28/08/2026