Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Proyecto III (exprés): El Organizador de Archivos

Antes y después del Organizador: la pila caótica de archivos termina en tres estantes rotulados

Fig. 13.2: El desorden entra, los estantes salen.

La carpeta de Descargas de cualquier computadora es un almacén sin gerente: facturas junto a memes, instaladores junto a fotos. Este proyecto la ordena: cada archivo a su estante según su extensión. Es el programa de automatización más gratificante que existe, cabe en veinte líneas, y todo lo que usa ya lo tienes.

El Reto

Dada una carpeta, el programa debe:

  1. Recorrer sus archivos (solo archivos, no subcarpetas).
  2. Decidir el estante según la extensión: .pdfdocumentos/, .jpg/.pngimagenes/, el resto → otros/.
  3. Crear los estantes si no existen y mover cada archivo al suyo.

El Plano

Un diccionario decide los destinos y pathlib hace el trabajo pesado:

"""Organizador: cada archivo a su estante según la extensión."""
from pathlib import Path

DESTINOS = {
    ".pdf": "documentos",
    ".txt": "documentos",
    ".jpg": "imagenes",
    ".png": "imagenes",
}

def organizar(carpeta: Path) -> int:
    """Mueve cada archivo a su subcarpeta y devuelve cuántos movió."""
    movidos = 0
    for archivo in carpeta.iterdir():
        if not archivo.is_file():
            continue  # las subcarpetas no se tocan

        estante = DESTINOS.get(archivo.suffix.lower(), "otros")
        destino = carpeta / estante
        destino.mkdir(exist_ok=True)

        archivo.rename(destino / archivo.name)
        movidos += 1
    return movidos


if __name__ == "__main__":
    carpeta = Path("desorden")
    total = organizar(carpeta)
    print(f"Organización terminada: {total} archivo(s) en su estante.")

Las Piezas, Una a Una

Casi nada aquí es nuevo (solo is_file() y rename(), y ambos se explican aquí mismo); el mérito está en el ensamble:

  • iterdir() recorre la carpeta (el for del Capítulo 8 sobre un territorio nuevo), y el continue del patrón de filtrado salta lo que no es archivo.
  • archivo.suffix es la extensión con punto (.pdf); en minúsculas para que .PDF no se escape (el .lower() de siempre).
  • DESTINOS.get(..., "otros") es el .get() con valor por defecto del Capítulo 5: los formatos desconocidos no rompen el programa, van a otros/.
  • mkdir(exist_ok=True) crea el estante solo si hace falta, sin quejarse si ya existe.
  • rename() es el montacargas: mueve el archivo a su destino, pero a diferencia de uno real, no frena si el destino ya está ocupado (ver la advertencia abajo).
  • Y el guardián if __name__ == "__main__": que conociste en el ensamblaje del SGI.

Pruébalo Sin Miedo

Mover archivos impone respeto, así que el protocolo de prueba es fabricar un desorden desechable:

En macOS/Linux:

mkdir desorden
cd desorden
touch factura.pdf notas.txt foto.jpg logo.png video.mp4
cd ..

En Windows (PowerShell):

mkdir desorden
cd desorden
ni factura.pdf, notas.txt, foto.jpg, logo.png, video.mp4
cd ..

Antes de correrlo, ¿a qué carpeta crees que va cada archivo? Repasa el diccionario DESTINOS de arriba.

Corre el programa y revisa el resultado: documentos/ con dos archivos, imagenes/ con dos, otros/ con el video. Si algo sale mal, el desorden era de mentira y no perdiste nada.

Cuidado: rename() no pregunta. Si en el destino ya existe un archivo con el mismo nombre, lo reemplaza sin aviso. Para una carpeta personal real, la primera mejora de abajo deja de ser opcional.

Lleva más Lejos

  • Colisiones: antes de mover, revisa con .exists() si el destino ya tiene ese nombre, y renombra (foto_2.jpg) en vez de pisar.
  • Reporte: cuenta cuántos archivos fueron a cada estante (el patrón contador, ahora con un diccionario) e imprime el resumen.
  • Configuración externa: saca DESTINOS a un destinos.json y cárgalo con json.load: cambiar las reglas dejará de requerir tocar el código.

Resumen del Capítulo

  • Ninguno de los dos proyectos introduce conceptos nuevos: ambos aplican herramientas de capítulos anteriores a problemas distintos.
  • sorted(..., key=lambda par: par[1], reverse=True): key decide por qué campo ordenar, y una lambda es una función de una sola línea, sin nombre, para cuando no vale la pena escribir un def.
  • Path.is_file() distingue archivos de subcarpetas al recorrer con iterdir().
  • Path.rename() mueve un archivo a un destino nuevo, pero lo hace sin avisar si ya hay algo ahí.

Con el SGI, el Analizador y el Organizador terminados, ya construiste tres programas completos y distintos con la misma caja de herramientas. El último capítulo cierra el libro con un repaso de todo lo aprendido y un mapa de por dónde seguir.