El módulo os.path siempre fue funcional pero verboso. Desde Python 3.4, pathlib.Path proporciona una API orientada a objetos para trabajar con rutas: los paths se combinan con el operador /, los métodos son autodescriptivos y el código resultante es más legible y portable entre sistemas operativos.
Crear y combinar paths
from pathlib import Path
# Crear paths
base = Path("/var/www/proyecto")
config = base / "config" / "settings.json" # combinar con /
print(config) # /var/www/proyecto/config/settings.json
# Path relativo al directorio actual
raiz = Path(".")
src = raiz / "src" / "main.py"
# Path al directorio home del usuario
home = Path.home()
print(home) # /root (o /home/usuario)
# Path al directorio del script actual
aqui = Path(__file__).parent
print(aqui)
Componentes de un path
from pathlib import Path
p = Path("/var/www/proyecto/src/utils.py")
print(p.name) # utils.py (nombre con extensión)
print(p.stem) # utils (nombre sin extensión)
print(p.suffix) # .py (extensión)
print(p.suffixes) # ['.py'] (lista de extensiones, útil para .tar.gz)
print(p.parent) # /var/www/proyecto/src
print(p.parents) # [/var/www/proyecto/src, /var/www/proyecto, /var/www, /var, /]
print(p.parts) # ('/', 'var', 'www', 'proyecto', 'src', 'utils.py')
print(p.root) # /
# Cambiar extensión
nuevo = p.with_suffix(".txt")
print(nuevo) # /var/www/proyecto/src/utils.txt
# Cambiar nombre
otro = p.with_name("helpers.py")
print(otro) # /var/www/proyecto/src/helpers.py
Leer y escribir ficheros
from pathlib import Path
ruta = Path("datos.txt")
# Escribir (crea o sobreescribe)
ruta.write_text("Hola, mundonSegunda línean", encoding="utf-8")
# Leer completo
contenido = ruta.read_text(encoding="utf-8")
print(contenido)
# Bytes
ruta_bin = Path("imagen.bin")
ruta_bin.write_bytes(b"x89PNGrn")
datos = ruta_bin.read_bytes()
# Abrir con open() (también funciona con Path)
with ruta.open("a", encoding="utf-8") as f:
f.write("Tercera línean")
mkdir, iterdir y operaciones de directorio
from pathlib import Path
# Crear directorios
directorio = Path("mi_proyecto/src/utils")
directorio.mkdir(parents=True, exist_ok=True)
# parents=True: crea intermedios si no existen
# exist_ok=True: no lanza error si ya existe
# Comprobar existencia y tipo
p = Path("mi_archivo.txt")
print(p.exists()) # True/False
print(p.is_file()) # True si es fichero
print(p.is_dir()) # True si es directorio
# Listar contenido de un directorio
directorio = Path("/tmp")
for elemento in directorio.iterdir():
tipo = "DIR" if elemento.is_dir() else "FILE"
print(f"[{tipo}] {elemento.name}")
# Renombrar y mover
p.rename(Path("nuevo_nombre.txt"))
p.replace(Path("/otro/directorio/fichero.txt")) # mueve y sobreescribe si existe
# Eliminar
p.unlink(missing_ok=True) # elimina fichero (missing_ok evita error si no existe)
directorio_vacio = Path("temporal")
directorio_vacio.rmdir() # solo si está vacío
import shutil
shutil.rmtree(Path("directorio_completo")) # elimina árbol completo
glob buscar ficheros por patrón
from pathlib import Path
proyecto = Path(".")
# Buscar todos los .py en el directorio actual (no recursivo)
for py in proyecto.glob("*.py"):
print(py.name)
# Búsqueda recursiva con **
for py in proyecto.rglob("*.py"):
print(py) # todos los .py en cualquier subdirectorio
# Patrones más específicos
for config in proyecto.glob("**/config*.json"):
print(config)
# Filtrar y ordenar
archivos_py = sorted(proyecto.rglob("*.py"), key=lambda p: p.stat().st_size)
for f in archivos_py[-5:]: # los 5 más grandes
print(f"{f}: {f.stat().st_size:,} bytes")
Información del fichero con stat()
from pathlib import Path
from datetime import datetime
p = Path("mi_fichero.txt")
p.write_text("contenido de prueba")
info = p.stat()
print(f"Tamaño: {info.st_size} bytes")
print(f"Modificado: {datetime.fromtimestamp(info.st_mtime)}")
print(f"Creado: {datetime.fromtimestamp(info.st_ctime)}")
Comparación con os.path
import os
from pathlib import Path
# os.path (más verboso)
ruta = os.path.join("/var/www", "proyecto", "main.py")
nombre = os.path.basename(ruta)
existe = os.path.exists(ruta)
directorio = os.path.dirname(ruta)
# pathlib (más expresivo)
ruta = Path("/var/www") / "proyecto" / "main.py"
nombre = ruta.name
existe = ruta.exists()
directorio = ruta.parent
# La diferencia se nota más en operaciones encadenadas
# os.path: os.path.join(os.path.dirname(os.path.abspath(__file__)), "config.json")
# pathlib: Path(__file__).parent / "config.json"
La regla práctica: usa pathlib para todo código nuevo. Es más legible, más expresivo y portable entre Windows (que usa ) y Unix (que usa /) sin ningún cambio, porque Python siempre muestra el separador correcto para el sistema. El operador / para concatenar paths es especialmente útil porque hace el código leíble como una ruta real.
