Python
Tutorial Python: Módulo pathlib
Aprende a usar el módulo pathlib en Python para manipular rutas y archivos con objetos, mejorando la portabilidad y legibilidad del código.
Aprende Python y certifícateRutas como objetos
En Python, tradicionalmente hemos manipulado rutas de archivos y directorios como simples cadenas de texto. Sin embargo, este enfoque presenta varios inconvenientes: concatenar rutas correctamente entre diferentes sistemas operativos, manejar barras separadoras, y realizar operaciones comunes como verificar la existencia de archivos requiere código adicional y propenso a errores.
El módulo pathlib, introducido en Python 3.4 y mejorado significativamente en versiones posteriores, ofrece una solución elegante a estos problemas al representar las rutas como objetos en lugar de cadenas. Este enfoque orientado a objetos transforma completamente la forma en que trabajamos con el sistema de archivos.
Conceptos básicos de pathlib
Para empezar a utilizar pathlib, primero debemos importarlo:
from pathlib import Path
La clase Path
es el componente central de pathlib. Cuando creamos un objeto Path, obtenemos una representación de una ruta del sistema de archivos que podemos manipular mediante métodos y propiedades:
# Crear un objeto Path para el directorio actual
ruta_actual = Path('.')
print(ruta_actual) # Muestra: .
# Crear un objeto Path para una ruta específica
ruta_documento = Path('/home/usuario/documentos/archivo.txt')
print(ruta_documento) # Muestra: /home/usuario/documentos/archivo.txt
Una ventaja inmediata es que pathlib detecta automáticamente el sistema operativo y utiliza el separador de rutas adecuado (barra diagonal /
en Unix/Linux/macOS o barra invertida \
en Windows).
Creación de rutas
Podemos crear objetos Path de varias maneras:
# Ruta absoluta
ruta1 = Path('/home/usuario/documentos')
# Ruta relativa
ruta2 = Path('documentos/archivos')
# Desde partes de ruta
ruta3 = Path('home', 'usuario', 'documentos')
Una característica poderosa de pathlib es la capacidad de construir rutas mediante el operador /
:
# Construir una ruta combinando objetos Path
base = Path('/home/usuario')
completa = base / 'documentos' / 'archivo.txt'
print(completa) # Muestra: /home/usuario/documentos/archivo.txt
Este enfoque es mucho más legible y menos propenso a errores que concatenar cadenas o usar os.path.join()
.
Acceso a componentes de la ruta
Los objetos Path proporcionan propiedades para acceder a diferentes componentes de una ruta:
ruta = Path('/home/usuario/documentos/informe.txt')
# Obtener el nombre del archivo
print(ruta.name) # Muestra: informe.txt
# Obtener la extensión del archivo
print(ruta.suffix) # Muestra: .txt
# Obtener el nombre sin extensión
print(ruta.stem) # Muestra: informe
# Obtener el directorio padre
print(ruta.parent) # Muestra: /home/usuario/documentos
También podemos acceder a múltiples niveles de directorios padres:
# Obtener el padre del padre (dos niveles arriba)
print(ruta.parent.parent) # Muestra: /home/usuario
Resolución de rutas
Los objetos Path pueden resolver rutas relativas y absolutas:
# Convertir una ruta relativa en absoluta
ruta_relativa = Path('documentos/archivo.txt')
ruta_absoluta = ruta_relativa.absolute()
print(ruta_absoluta) # Muestra la ruta absoluta completa
# Resolver una ruta (elimina componentes como '..' y '.')
ruta_confusa = Path('/home/usuario/../usuario/./documentos')
ruta_resuelta = ruta_confusa.resolve()
print(ruta_resuelta) # Muestra: /home/usuario/documentos
Rutas especiales
Pathlib facilita el acceso a rutas especiales del sistema:
# Directorio de inicio del usuario (home)
home = Path.home()
print(home) # Muestra: /home/usuario (en Linux/macOS) o C:\Users\usuario (en Windows)
# Directorio de trabajo actual
actual = Path.cwd()
print(actual) # Muestra el directorio desde donde se ejecuta el script
Propiedades de los objetos Path
Los objetos Path tienen propiedades que nos permiten consultar información sobre la ruta:
ruta = Path('/home/usuario/documentos/archivo.txt')
# Verificar si la ruta es absoluta
print(ruta.is_absolute()) # Muestra: True
# Verificar si la ruta apunta a un archivo (si existe)
print(Path('/etc/passwd').is_file()) # Muestra: True si el archivo existe
# Verificar si la ruta apunta a un directorio (si existe)
print(Path('/home').is_dir()) # Muestra: True si el directorio existe
# Verificar si la ruta existe
print(ruta.exists()) # Muestra: True si existe, False si no
Iteración sobre directorios
Una ventaja significativa de pathlib es la facilidad para iterar sobre el contenido de directorios:
# Listar todos los archivos y directorios en una ruta
for item in Path('.').iterdir():
print(item)
# Listar solo archivos Python en el directorio actual
for archivo_py in Path('.').glob('*.py'):
print(archivo_py)
# Búsqueda recursiva de archivos
for archivo_txt in Path('.').rglob('*.txt'):
print(archivo_txt)
El método glob()
permite filtrar archivos usando patrones, mientras que rglob()
realiza una búsqueda recursiva en subdirectorios.
Compatibilidad entre sistemas operativos
Una de las mayores ventajas de pathlib es su manejo transparente de las diferencias entre sistemas operativos:
# Esta ruta funcionará correctamente en Windows, macOS y Linux
ruta = Path('documentos') / 'archivos' / 'datos.csv'
Pathlib se encarga automáticamente de usar el separador correcto según el sistema operativo, lo que hace que nuestro código sea más portable.
Operaciones con rutas
Una vez que hemos creado objetos Path, podemos realizar numerosas operaciones con ellos sin necesidad de importar módulos adicionales. El módulo pathlib proporciona métodos intuitivos que simplifican tareas comunes de manipulación de archivos y directorios.
Comprobación de existencia y tipo
Antes de realizar operaciones sobre archivos o directorios, a menudo necesitamos verificar su existencia y tipo:
from pathlib import Path
ruta = Path('datos/config.ini')
# Comprobar si existe
if ruta.exists():
print("La ruta existe")
# Comprobar el tipo de la ruta
if ruta.is_file():
print("Es un archivo")
elif ruta.is_dir():
print("Es un directorio")
elif ruta.is_symlink():
print("Es un enlace simbólico")
También podemos verificar permisos específicos:
# Comprobar si se puede leer
if ruta.is_file() and os.access(ruta, os.R_OK):
print("El archivo se puede leer")
# Comprobar si se puede ejecutar
if ruta.is_file() and ruta.with_suffix('.py').exists() and os.access(ruta, os.X_OK):
print("El archivo Python se puede ejecutar")
Modificación de rutas
Pathlib ofrece métodos para modificar rutas sin alterar los archivos subyacentes:
ruta = Path('/home/usuario/documentos/informe.txt')
# Cambiar la extensión
nueva_ruta = ruta.with_suffix('.pdf')
print(nueva_ruta) # Muestra: /home/usuario/documentos/informe.pdf
# Cambiar el nombre manteniendo el directorio
otra_ruta = ruta.with_name('reporte.txt')
print(otra_ruta) # Muestra: /home/usuario/documentos/reporte.txt
# Cambiar solo una parte del nombre (stem)
ruta_modificada = ruta.with_stem('informe_final')
print(ruta_modificada) # Muestra: /home/usuario/documentos/informe_final.txt
Operaciones de lectura y escritura
Los objetos Path incluyen métodos para leer y escribir archivos directamente:
# Leer todo el contenido como texto
ruta = Path('config.ini')
contenido = ruta.read_text(encoding='utf-8')
print(contenido)
# Leer todo el contenido como bytes
datos_binarios = ruta.read_bytes()
# Escribir texto a un archivo
ruta_salida = Path('salida.txt')
ruta_salida.write_text('Contenido nuevo', encoding='utf-8')
# Escribir bytes a un archivo
ruta_binaria = Path('datos.bin')
ruta_binaria.write_bytes(b'\x00\x01\x02\x03')
Estos métodos son perfectos para operaciones simples. Para casos más complejos, podemos usar los objetos Path con las funciones tradicionales de apertura de archivos:
# Usar Path con open()
with open(ruta, 'r', encoding='utf-8') as f:
for linea in f:
print(linea.strip())
Creación y eliminación de archivos y directorios
Pathlib facilita la creación y eliminación de elementos del sistema de archivos:
# Crear un directorio
directorio = Path('nuevos_datos')
directorio.mkdir(exist_ok=True) # No falla si ya existe
# Crear directorios anidados
ruta_anidada = Path('proyectos/python/scripts')
ruta_anidada.mkdir(parents=True, exist_ok=True)
# Crear un archivo vacío o actualizar su timestamp
archivo = Path('archivo_vacio.txt')
archivo.touch()
# Eliminar un archivo
if archivo.exists():
archivo.unlink()
# Eliminar un directorio (debe estar vacío)
if directorio.exists() and directorio.is_dir():
directorio.rmdir()
Para operaciones más complejas como eliminar directorios no vacíos, podemos combinar pathlib con otras funciones:
import shutil
# Eliminar un directorio y todo su contenido
directorio_completo = Path('directorio_con_archivos')
if directorio_completo.exists():
shutil.rmtree(directorio_completo)
Renombrar y mover archivos
Pathlib permite renombrar y mover archivos o directorios de forma intuitiva:
# Renombrar un archivo
origen = Path('documento_viejo.txt')
destino = Path('documento_nuevo.txt')
if origen.exists():
origen.rename(destino)
# Mover un archivo a otro directorio
archivo = Path('informe.pdf')
nuevo_directorio = Path('archivados')
if nuevo_directorio.exists() and nuevo_directorio.is_dir():
archivo.rename(nuevo_directorio / archivo.name)
Copia de archivos
Aunque pathlib no incluye métodos directos para copiar archivos, podemos combinarlo fácilmente con el módulo shutil:
import shutil
# Copiar un archivo
origen = Path('datos.csv')
destino = Path('respaldo/datos.csv')
# Asegurar que el directorio destino existe
destino.parent.mkdir(parents=True, exist_ok=True)
# Realizar la copia
if origen.exists():
shutil.copy2(origen, destino) # copy2 preserva metadatos
Búsqueda de archivos
Los métodos de búsqueda de pathlib son muy potentes para encontrar archivos que cumplan ciertos criterios:
# Encontrar todos los archivos Python en el directorio actual
archivos_py = list(Path('.').glob('*.py'))
# Búsqueda recursiva de archivos de configuración
archivos_config = list(Path('.').rglob('*.ini'))
# Búsqueda con patrones más complejos
imagenes = list(Path('documentos').glob('**/*.{jpg,png,gif}'))
# Filtrar resultados con comprensión de listas
archivos_grandes = [
archivo for archivo in Path('.').rglob('*.txt')
if archivo.is_file() and archivo.stat().st_size > 1000000
]
Obtención de metadatos
Podemos acceder a información detallada sobre archivos y directorios:
archivo = Path('documento.pdf')
if archivo.exists():
# Obtener estadísticas del archivo
stats = archivo.stat()
# Tamaño en bytes
print(f"Tamaño: {stats.st_size} bytes")
# Fechas de acceso, modificación y creación
from datetime import datetime
print(f"Último acceso: {datetime.fromtimestamp(stats.st_atime)}")
print(f"Última modificación: {datetime.fromtimestamp(stats.st_mtime)}")
print(f"Creación: {datetime.fromtimestamp(stats.st_ctime)}")
# Permisos (en formato octal)
print(f"Permisos: {oct(stats.st_mode)[-3:]}")
Operaciones con rutas relativas
Pathlib facilita el trabajo con rutas relativas entre diferentes ubicaciones:
# Obtener la ruta relativa desde una ubicación a otra
base = Path('/home/usuario')
destino = Path('/home/usuario/proyectos/python/script.py')
ruta_relativa = destino.relative_to(base)
print(ruta_relativa) # Muestra: proyectos/python/script.py
# Comprobar si una ruta es subpath de otra
try:
Path('/tmp/archivo.txt').relative_to('/home')
print("Es un subpath")
except ValueError:
print("No es un subpath") # Este será el resultado
Comparación de rutas
Los objetos Path se pueden comparar directamente, lo que facilita verificar si dos rutas apuntan al mismo lugar:
ruta1 = Path('/home/usuario/../usuario/documentos')
ruta2 = Path('/home/usuario/documentos')
# Comparación directa (sin resolver)
print(ruta1 == ruta2) # Muestra: False
# Comparación después de resolver
print(ruta1.resolve() == ruta2.resolve()) # Muestra: True
Conversión entre Path y string
A veces necesitamos convertir entre objetos Path y cadenas, especialmente al interactuar con APIs que no soportan pathlib:
# Convertir Path a string
ruta = Path('/home/usuario/archivo.txt')
ruta_str = str(ruta)
print(ruta_str) # Muestra: /home/usuario/archivo.txt
# Convertir string a Path
path_de_nuevo = Path(ruta_str)
Estas operaciones con rutas demuestran cómo pathlib simplifica significativamente el trabajo con el sistema de archivos, proporcionando una interfaz orientada a objetos que es más intuitiva y menos propensa a errores que las soluciones tradicionales basadas en cadenas.
Ventajas sobre os.path
El módulo pathlib representa una evolución significativa respecto al tradicional os.path que ha sido durante décadas la forma estándar de manipular rutas en Python. Aunque ambos cumplen propósitos similares, pathlib ofrece numerosas ventajas que lo convierten en la opción preferida para el desarrollo moderno en Python.
Sintaxis más clara y concisa
La diferencia más evidente entre ambos enfoques es la sintaxis. Mientras que os.path requiere llamadas a funciones separadas para cada operación, pathlib utiliza un enfoque orientado a objetos con métodos encadenables:
# Enfoque tradicional con os.path
import os
ruta_completa = os.path.join(os.path.expanduser('~'), 'documentos', 'archivo.txt')
nombre = os.path.basename(ruta_completa)
extension = os.path.splitext(nombre)[1]
# Enfoque moderno con pathlib
from pathlib import Path
ruta_completa = Path.home() / 'documentos' / 'archivo.txt'
nombre = ruta_completa.name
extension = ruta_completa.suffix
Esta sintaxis más fluida de pathlib no solo reduce la cantidad de código, sino que también mejora significativamente su legibilidad.
Operador de división para construir rutas
Una de las características más elegantes de pathlib es el uso del operador /
para construir rutas:
# Con os.path (propenso a errores)
ruta = os.path.join(os.path.join('directorio', 'subdirectorio'), 'archivo.txt')
# Con pathlib (intuitivo y claro)
ruta = Path('directorio') / 'subdirectorio' / 'archivo.txt'
Este enfoque es más natural, ya que refleja la notación habitual de rutas en sistemas Unix, pero funciona correctamente en todos los sistemas operativos.
Métodos integrados para operaciones comunes
Con os.path, las operaciones de sistema de archivos requieren importar y combinar múltiples módulos:
# Enfoque tradicional con múltiples módulos
import os
import shutil
import glob
# Verificar existencia
if os.path.exists(ruta) and os.path.isfile(ruta):
# Leer contenido
with open(ruta, 'r') as f:
contenido = f.read()
# Buscar archivos
archivos_txt = glob.glob(os.path.join(directorio, '*.txt'))
Con pathlib, estas operaciones están integradas en la misma interfaz:
# Enfoque unificado con pathlib
from pathlib import Path
ruta = Path('archivo.txt')
# Verificar existencia
if ruta.exists() and ruta.is_file():
# Leer contenido
contenido = ruta.read_text()
# Buscar archivos
archivos_txt = list(Path(directorio).glob('*.txt'))
Esta integración reduce la necesidad de importar múltiples módulos y proporciona una API más coherente.
Compatibilidad entre sistemas operativos
Aunque os.path intenta manejar las diferencias entre sistemas operativos, pathlib lo hace de manera más transparente:
# os.path requiere cuidado con los separadores
windows_path = 'C:\\Users\\usuario\\documentos'
unix_path = '/home/usuario/documentos'
# pathlib maneja esto automáticamente
windows_path = Path('C:/Users/usuario/documentos') # Funciona en Windows
unix_path = Path('/home/usuario/documentos') # Funciona en Unix/Linux
Pathlib normaliza automáticamente los separadores según el sistema operativo, lo que hace que el código sea más portable sin esfuerzo adicional.
Métodos específicos para componentes de ruta
Con os.path, extraer componentes de una ruta requiere múltiples llamadas a funciones:
# Extraer componentes con os.path
ruta = '/home/usuario/documentos/informe.txt'
directorio = os.path.dirname(ruta)
nombre = os.path.basename(ruta)
nombre_sin_ext = os.path.splitext(nombre)[0]
extension = os.path.splitext(nombre)[1]
Pathlib proporciona propiedades específicas para cada componente:
# Extraer componentes con pathlib
ruta = Path('/home/usuario/documentos/informe.txt')
directorio = ruta.parent
nombre = ruta.name
nombre_sin_ext = ruta.stem
extension = ruta.suffix
Este enfoque es más intuitivo y menos propenso a errores.
Operaciones de E/S integradas
Una ventaja significativa de pathlib es la inclusión de métodos para operaciones de entrada/salida:
# Con os.path y funciones estándar
import os
ruta = 'datos.txt'
# Leer
with open(ruta, 'r', encoding='utf-8') as f:
contenido = f.read()
# Escribir
with open(ruta, 'w', encoding='utf-8') as f:
f.write('Nuevos datos')
# Con pathlib
from pathlib import Path
ruta = Path('datos.txt')
# Leer
contenido = ruta.read_text(encoding='utf-8')
# Escribir
ruta.write_text('Nuevos datos', encoding='utf-8')
Esta integración elimina la necesidad de usar bloques with
para operaciones simples de lectura y escritura.
Iteración y filtrado de directorios
La búsqueda y filtrado de archivos es mucho más elegante con pathlib:
# Con os y glob
import os
import glob
# Listar archivos
archivos = os.listdir('.')
# Filtrar por extensión
archivos_py = glob.glob('*.py')
# Búsqueda recursiva
archivos_txt_recursivos = glob.glob('**/*.txt', recursive=True)
# Con pathlib
from pathlib import Path
# Listar archivos
archivos = list(Path('.').iterdir())
# Filtrar por extensión
archivos_py = list(Path('.').glob('*.py'))
# Búsqueda recursiva
archivos_txt_recursivos = list(Path('.').rglob('*.txt'))
Los métodos iterdir()
, glob()
y rglob()
de pathlib proporcionan una forma más coherente y potente de trabajar con colecciones de archivos.
Rendimiento y optimización
En versiones recientes de Python, pathlib ha sido optimizado para ofrecer un rendimiento comparable o incluso superior a os.path en muchos casos:
# Ejemplo de rendimiento: verificar existencia de múltiples archivos
import time
import os
from pathlib import Path
# Preparar lista de rutas
rutas = [f'archivo_{i}.txt' for i in range(1000)]
# Medir con os.path
inicio = time.time()
for ruta in rutas:
os.path.exists(ruta)
tiempo_os = time.time() - inicio
# Medir con pathlib
inicio = time.time()
for ruta in rutas:
Path(ruta).exists()
tiempo_pathlib = time.time() - inicio
print(f"Tiempo os.path: {tiempo_os:.6f}s")
print(f"Tiempo pathlib: {tiempo_pathlib:.6f}s")
Además, pathlib implementa un sistema de caché interno para algunas operaciones, lo que puede mejorar el rendimiento en escenarios donde se accede repetidamente a la misma información de un archivo.
Integración con otras bibliotecas
Desde Python 3.6, muchas funciones de la biblioteca estándar que antes solo aceptaban cadenas de texto ahora también aceptan objetos Path:
# Abrir un archivo
with open(Path('archivo.txt'), 'r') as f:
contenido = f.read()
# Usar con otras funciones
import json
datos = json.loads(Path('datos.json').read_text())
Esta integración elimina la necesidad de convertir constantemente entre objetos Path y cadenas.
Manejo de rutas especiales
Pathlib proporciona métodos convenientes para acceder a rutas especiales del sistema:
# Con os.path
import os
home = os.path.expanduser('~')
actual = os.getcwd()
# Con pathlib
from pathlib import Path
home = Path.home()
actual = Path.cwd()
Estos métodos hacen que el código sea más claro y expresivo.
Conclusión práctica
Para ilustrar las ventajas de pathlib en un caso de uso real, comparemos cómo se implementaría una función para buscar y procesar archivos:
# Implementación con os.path
import os
import glob
def procesar_logs_os(directorio):
"""Procesa archivos de log en el directorio especificado."""
if not os.path.exists(directorio) or not os.path.isdir(directorio):
raise ValueError(f"El directorio {directorio} no existe")
archivos_log = glob.glob(os.path.join(directorio, '*.log'))
resultados = []
for archivo in archivos_log:
if os.path.getsize(archivo) > 0:
with open(archivo, 'r') as f:
primera_linea = f.readline().strip()
nombre_base = os.path.basename(archivo)
resultados.append((nombre_base, primera_linea))
return resultados
# Implementación con pathlib
from pathlib import Path
def procesar_logs_pathlib(directorio):
"""Procesa archivos de log en el directorio especificado."""
dir_path = Path(directorio)
if not dir_path.exists() or not dir_path.is_dir():
raise ValueError(f"El directorio {directorio} no existe")
resultados = []
for archivo in dir_path.glob('*.log'):
if archivo.stat().st_size > 0:
primera_linea = archivo.read_text().splitlines()[0]
resultados.append((archivo.name, primera_linea))
return resultados
La versión con pathlib es más concisa, más legible y menos propensa a errores, lo que demuestra por qué se ha convertido en la opción recomendada para el manejo de rutas en Python moderno.
Manipulación de archivos y directorios
El módulo pathlib no solo simplifica la representación de rutas, sino que también proporciona una interfaz completa para manipular archivos y directorios de forma intuitiva. Esta funcionalidad nos permite realizar operaciones comunes del sistema de archivos sin necesidad de recurrir a módulos adicionales en muchos casos.
Lectura y escritura de archivos
Pathlib ofrece métodos directos para las operaciones más comunes de lectura y escritura:
from pathlib import Path
# Leer el contenido completo de un archivo como texto
ruta_config = Path('configuracion.ini')
contenido = ruta_config.read_text(encoding='utf-8')
print(f"Configuración cargada: {len(contenido)} caracteres")
# Leer el contenido como líneas individuales
lineas = contenido.splitlines()
for i, linea in enumerate(lineas[:5], 1):
print(f"Línea {i}: {linea}")
# Escribir texto a un archivo (crea o sobrescribe)
ruta_salida = Path('salida.log')
ruta_salida.write_text('Registro iniciado\nOperación completada\n', encoding='utf-8')
Para archivos binarios, pathlib proporciona métodos específicos:
# Leer datos binarios
ruta_imagen = Path('imagen.png')
datos_binarios = ruta_imagen.read_bytes()
print(f"Tamaño de la imagen: {len(datos_binarios)} bytes")
# Escribir datos binarios
nueva_imagen = Path('copia_imagen.png')
nueva_imagen.write_bytes(datos_binarios)
Para operaciones más complejas o procesamiento línea por línea, podemos combinar objetos Path con la función open()
:
# Procesar un archivo línea por línea de forma eficiente
archivo_grande = Path('datos_extensos.csv')
total_lineas = 0
with open(archivo_grande, 'r', encoding='utf-8') as f:
for linea in f:
total_lineas += 1
# Procesar cada línea sin cargar todo el archivo en memoria
print(f"Total de líneas procesadas: {total_lineas}")
Creación y gestión de archivos
Pathlib facilita la creación y manipulación de archivos:
# Crear un archivo vacío o actualizar su timestamp
archivo_log = Path('app.log')
archivo_log.touch(exist_ok=True) # No falla si ya existe
# Verificar si podemos escribir en el archivo
if os.access(archivo_log, os.W_OK):
archivo_log.write_text('Nueva entrada de log\n', encoding='utf-8')
# Renombrar un archivo
if archivo_log.exists():
nuevo_nombre = archivo_log.with_name('app_old.log')
archivo_log.rename(nuevo_nombre)
print(f"Archivo renombrado a: {nuevo_nombre}")
# Eliminar un archivo
archivo_temporal = Path('temp.dat')
if archivo_temporal.exists():
archivo_temporal.unlink()
print(f"Archivo {archivo_temporal} eliminado")
Para operaciones condicionales, podemos usar un bloque try-except:
# Eliminar un archivo de forma segura
archivo_cache = Path('cache.tmp')
try:
archivo_cache.unlink(missing_ok=True) # Python 3.8+
print("Cache eliminado o no existía")
except FileNotFoundError:
# En versiones anteriores a Python 3.8
print("El archivo no existía")
Gestión de directorios
La creación y manipulación de directorios es igualmente sencilla con pathlib:
# Crear un directorio
directorio_datos = Path('datos')
directorio_datos.mkdir(exist_ok=True) # No falla si ya existe
# Crear estructura de directorios anidados
ruta_proyecto = Path('proyecto/src/modulos')
ruta_proyecto.mkdir(parents=True, exist_ok=True)
# Eliminar un directorio (debe estar vacío)
directorio_temp = Path('temp')
if directorio_temp.exists():
directorio_temp.rmdir()
print(f"Directorio {directorio_temp} eliminado")
Para operaciones más complejas con directorios, como eliminar directorios no vacíos, necesitamos combinar pathlib con el módulo shutil:
import shutil
# Eliminar un directorio y todo su contenido
directorio_obsoleto = Path('old_data')
if directorio_obsoleto.exists():
shutil.rmtree(directorio_obsoleto)
print(f"Directorio {directorio_obsoleto} y su contenido eliminados")
Copiado de archivos y directorios
Aunque pathlib no incluye métodos directos para copiar archivos, podemos combinarlo fácilmente con shutil:
import shutil
# Copiar un archivo preservando metadatos
archivo_original = Path('documento.pdf')
archivo_copia = Path('backup/documento.pdf')
# Asegurar que el directorio destino existe
archivo_copia.parent.mkdir(parents=True, exist_ok=True)
if archivo_original.exists():
shutil.copy2(archivo_original, archivo_copia)
print(f"Archivo copiado a {archivo_copia}")
# Copiar un directorio completo
dir_origen = Path('proyecto')
dir_destino = Path('backup/proyecto')
if dir_origen.exists() and dir_origen.is_dir():
shutil.copytree(dir_origen, dir_destino, dirs_exist_ok=True) # dirs_exist_ok es para Python 3.8+
print(f"Directorio copiado a {dir_destino}")
Navegación y exploración del sistema de archivos
Pathlib facilita la navegación y exploración del sistema de archivos:
# Listar todos los elementos en un directorio
directorio = Path('proyecto')
print("Contenido del directorio:")
for item in directorio.iterdir():
tipo = "Directorio" if item.is_dir() else "Archivo"
print(f"- {item.name} ({tipo})")
# Filtrar archivos por extensión
print("\nArchivos Python:")
for archivo_py in directorio.glob('*.py'):
print(f"- {archivo_py.name}")
# Búsqueda recursiva
print("\nTodos los archivos de configuración:")
for config in directorio.rglob('*.json'):
print(f"- {config.relative_to(directorio)}")
Para casos más específicos, podemos combinar los métodos de búsqueda con comprensiones de lista:
# Encontrar archivos modificados en las últimas 24 horas
import time
from datetime import datetime, timedelta
ayer = datetime.now() - timedelta(days=1)
timestamp_ayer = ayer.timestamp()
archivos_recientes = [
archivo for archivo in Path('.').rglob('*.*')
if archivo.is_file() and archivo.stat().st_mtime > timestamp_ayer
]
print(f"\nArchivos modificados en las últimas 24 horas: {len(archivos_recientes)}")
for archivo in archivos_recientes[:5]: # Mostrar solo los primeros 5
mod_time = datetime.fromtimestamp(archivo.stat().st_mtime)
print(f"- {archivo.name} (Modificado: {mod_time.strftime('%Y-%m-%d %H:%M:%S')})")
Gestión de permisos y propietarios
Para manipular permisos de archivos, podemos combinar pathlib con funciones del módulo os:
import os
import stat
# Obtener permisos actuales
archivo = Path('script.py')
if archivo.exists():
permisos = archivo.stat().st_mode
print(f"Permisos actuales: {oct(permisos)}")
# Hacer un archivo ejecutable
os.chmod(archivo, permisos | stat.S_IXUSR | stat.S_IXGRP)
print("Archivo marcado como ejecutable")
# Verificar si ahora es ejecutable
if os.access(archivo, os.X_OK):
print("Confirmado: el archivo es ejecutable")
Monitoreo de archivos y directorios
Podemos usar pathlib para implementar funciones de monitoreo básicas:
import time
from datetime import datetime
# Función para monitorear cambios en un directorio
def monitorear_directorio(ruta, intervalo=5):
directorio = Path(ruta)
if not directorio.exists() or not directorio.is_dir():
raise ValueError(f"{ruta} no es un directorio válido")
# Capturar estado inicial
archivos_previos = {archivo: archivo.stat().st_mtime
for archivo in directorio.glob('*.*') if archivo.is_file()}
print(f"Monitoreando {directorio}. Presiona Ctrl+C para detener.")
try:
while True:
time.sleep(intervalo)
# Capturar estado actual
archivos_actuales = {archivo: archivo.stat().st_mtime
for archivo in directorio.glob('*.*') if archivo.is_file()}
# Detectar archivos nuevos
nuevos = set(archivos_actuales.keys()) - set(archivos_previos.keys())
for archivo in nuevos:
print(f"[{datetime.now().strftime('%H:%M:%S')}] NUEVO: {archivo.name}")
# Detectar archivos modificados
for archivo in set(archivos_actuales.keys()) & set(archivos_previos.keys()):
if archivos_actuales[archivo] > archivos_previos[archivo]:
print(f"[{datetime.now().strftime('%H:%M:%S')}] MODIFICADO: {archivo.name}")
# Detectar archivos eliminados
eliminados = set(archivos_previos.keys()) - set(archivos_actuales.keys())
for archivo in eliminados:
print(f"[{datetime.now().strftime('%H:%M:%S')}] ELIMINADO: {archivo.name}")
# Actualizar estado previo
archivos_previos = archivos_actuales
except KeyboardInterrupt:
print("\nMonitoreo finalizado.")
# Uso:
# monitorear_directorio('proyecto/src')
Casos de uso prácticos
Veamos algunos ejemplos prácticos que combinan varias operaciones:
1. Organizar archivos por tipo de extensión:
def organizar_por_extension(directorio):
"""Organiza archivos en subdirectorios según su extensión."""
dir_base = Path(directorio)
if not dir_base.exists() or not dir_base.is_dir():
raise ValueError(f"{directorio} no es un directorio válido")
# Encontrar todos los archivos (no directorios)
archivos = [f for f in dir_base.iterdir() if f.is_file()]
# Crear directorios para cada extensión y mover archivos
for archivo in archivos:
# Obtener extensión (sin el punto) o 'sin_extension'
extension = archivo.suffix[1:] if archivo.suffix else 'sin_extension'
# Crear directorio para esta extensión
dir_extension = dir_base / extension
dir_extension.mkdir(exist_ok=True)
# Definir ruta destino
destino = dir_extension / archivo.name
# Mover archivo
try:
archivo.rename(destino)
print(f"Movido: {archivo.name} → {extension}/{archivo.name}")
except Exception as e:
print(f"Error al mover {archivo.name}: {e}")
2. Crear una copia de seguridad de archivos importantes:
def crear_backup(directorio_origen, directorio_destino):
"""Crea una copia de seguridad de archivos importantes."""
origen = Path(directorio_origen)
destino = Path(directorio_destino)
# Verificar directorios
if not origen.exists() or not origen.is_dir():
raise ValueError(f"Directorio origen no válido: {origen}")
# Crear directorio destino si no existe
destino.mkdir(parents=True, exist_ok=True)
# Extensiones de archivos importantes
extensiones_importantes = {'.py', '.java', '.js', '.html', '.css', '.md', '.txt', '.pdf', '.docx'}
# Encontrar archivos importantes
archivos_importantes = [
archivo for archivo in origen.rglob('*')
if archivo.is_file() and archivo.suffix.lower() in extensiones_importantes
]
# Copiar archivos manteniendo la estructura de directorios
for archivo in archivos_importantes:
# Calcular ruta relativa
ruta_relativa = archivo.relative_to(origen)
ruta_destino = destino / ruta_relativa
# Crear directorios padres en destino
ruta_destino.parent.mkdir(parents=True, exist_ok=True)
# Copiar archivo
import shutil
shutil.copy2(archivo, ruta_destino)
print(f"Respaldado: {ruta_relativa}")
print(f"Backup completado: {len(archivos_importantes)} archivos copiados")
3. Limpiar archivos temporales:
def limpiar_temporales(directorio, dias=7):
"""Elimina archivos temporales más antiguos que el número de días especificado."""
dir_base = Path(directorio)
if not dir_base.exists() or not dir_base.is_dir():
raise ValueError(f"{directorio} no es un directorio válido")
# Calcular timestamp límite
import time
from datetime import datetime, timedelta
fecha_limite = datetime.now() - timedelta(days=dias)
timestamp_limite = fecha_limite.timestamp()
# Extensiones típicas de archivos temporales
extensiones_temp = {'.tmp', '.temp', '.bak', '.log', '.cache'}
# Encontrar y eliminar archivos antiguos
total_eliminados = 0
total_bytes_liberados = 0
for archivo in dir_base.rglob('*'):
if (archivo.is_file() and
(archivo.suffix.lower() in extensiones_temp or archivo.name.startswith('~')) and
archivo.stat().st_mtime < timestamp_limite):
# Guardar tamaño antes de eliminar
tamano = archivo.stat().st_size
# Eliminar archivo
try:
archivo.unlink()
total_eliminados += 1
total_bytes_liberados += tamano
print(f"Eliminado: {archivo}")
except Exception as e:
print(f"Error al eliminar {archivo}: {e}")
# Mostrar resumen
mb_liberados = total_bytes_liberados / (1024 * 1024)
print(f"\nLimpieza completada: {total_eliminados} archivos eliminados ({mb_liberados:.2f} MB liberados)")
Estos ejemplos demuestran cómo pathlib simplifica considerablemente la manipulación de archivos y directorios en Python, permitiéndonos escribir código más limpio, legible y mantenible para operaciones comunes del sistema de archivos.
Ejercicios de esta lección Módulo pathlib
Evalúa tus conocimientos de esta lección Módulo pathlib con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
Módulo math
Reto herencia
Excepciones
Introducción a Python
Reto variables
Funciones Python
Reto funciones
Módulo datetime
Reto acumulación
Reto estructuras condicionales
Polimorfismo
Módulo os
Reto métodos dunder
Diccionarios
Reto clases y objetos
Reto operadores
Operadores
Estructuras de control
Funciones lambda
Reto diccionarios
Reto función lambda
Encapsulación
Reto coleciones
Reto funciones auxiliares
Crear módulos y paquetes
Módulo datetime
Excepciones
Operadores
Diccionarios
Reto map, filter
Reto tuplas
Proyecto gestor de tareas CRUD
Tuplas
Variables
Tipos de datos
Conjuntos
Reto mixins
Módulo csv
Módulo json
Herencia
Análisis de datos de ventas con Pandas
Reto fechas y tiempo
Reto estructuras de iteración
Funciones
Reto comprehensions
Variables
Reto serialización
Módulo csv
Reto polimorfismo
Polimorfismo
Clases y objetos
Reto encapsulación
Estructuras de control
Importar módulos y paquetes
Módulo math
Funciones lambda
Reto excepciones
Listas
Reto archivos
Encapsulación
Reto conjuntos
Clases y objetos
Instalación de Python y creación de proyecto
Reto listas
Tipos de datos
Crear módulos y paquetes
Tuplas
Herencia
Reto acceso a sistema
Proyecto sintaxis calculadora
Importar módulos y paquetes
Clases y objetos
Módulo os
Listas
Conjuntos
Reto tipos de datos
Reto matemáticas
Módulo json
Todas las lecciones de Python
Accede a todas las lecciones de Python y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Python
Introducción
Instalación Y Creación De Proyecto
Introducción
Tema 2: Tipos De Datos, Variables Y Operadores
Introducción
Instalación De Python
Introducción
Tipos De Datos
Sintaxis
Variables
Sintaxis
Operadores
Sintaxis
Estructuras De Control
Sintaxis
Funciones
Sintaxis
Estructuras Control Iterativo
Sintaxis
Estructuras Control Condicional
Sintaxis
Testing Con Pytest
Sintaxis
Listas
Estructuras De Datos
Tuplas
Estructuras De Datos
Diccionarios
Estructuras De Datos
Conjuntos
Estructuras De Datos
Comprehensions
Estructuras De Datos
Clases Y Objetos
Programación Orientada A Objetos
Excepciones
Programación Orientada A Objetos
Encapsulación
Programación Orientada A Objetos
Herencia
Programación Orientada A Objetos
Polimorfismo
Programación Orientada A Objetos
Mixins Y Herencia Múltiple
Programación Orientada A Objetos
Métodos Especiales (Dunder Methods)
Programación Orientada A Objetos
Composición De Clases
Programación Orientada A Objetos
Funciones Lambda
Programación Funcional
Aplicación Parcial
Programación Funcional
Entrada Y Salida, Manejo De Archivos
Programación Funcional
Decoradores
Programación Funcional
Generadores
Programación Funcional
Paradigma Funcional
Programación Funcional
Composición De Funciones
Programación Funcional
Funciones Orden Superior Map Y Filter
Programación Funcional
Funciones Auxiliares
Programación Funcional
Reducción Y Acumulación
Programación Funcional
Archivos Comprimidos
Entrada Y Salida Io
Entrada Y Salida Avanzada
Entrada Y Salida Io
Archivos Temporales
Entrada Y Salida Io
Contexto With
Entrada Y Salida Io
Módulo Csv
Biblioteca Estándar
Módulo Json
Biblioteca Estándar
Módulo Datetime
Biblioteca Estándar
Módulo Math
Biblioteca Estándar
Módulo Os
Biblioteca Estándar
Módulo Re
Biblioteca Estándar
Módulo Random
Biblioteca Estándar
Módulo Time
Biblioteca Estándar
Módulo Collections
Biblioteca Estándar
Módulo Sys
Biblioteca Estándar
Módulo Statistics
Biblioteca Estándar
Módulo Pickle
Biblioteca Estándar
Módulo Pathlib
Biblioteca Estándar
Importar Módulos Y Paquetes
Paquetes Y Módulos
Crear Módulos Y Paquetes
Paquetes Y Módulos
Entornos Virtuales (Virtualenv, Venv)
Entorno Y Dependencias
Gestión De Dependencias (Pip, Requirements.txt)
Entorno Y Dependencias
Python-dotenv Y Variables De Entorno
Entorno Y Dependencias
Acceso A Datos Con Mysql, Pymongo Y Pandas
Acceso A Bases De Datos
Acceso A Mongodb Con Pymongo
Acceso A Bases De Datos
Acceso A Mysql Con Mysql Connector
Acceso A Bases De Datos
Novedades Python 3.13
Características Modernas
Operador Walrus
Características Modernas
Pattern Matching
Características Modernas
Instalación Beautiful Soup
Web Scraping
Sintaxis General De Beautiful Soup
Web Scraping
Tipos De Selectores
Web Scraping
Web Scraping De Html
Web Scraping
Web Scraping Para Ciencia De Datos
Web Scraping
Autenticación Y Acceso A Recursos Protegidos
Web Scraping
Combinación De Selenium Con Beautiful Soup
Web Scraping
En esta lección
Objetivos de aprendizaje de esta lección
- Comprender la representación de rutas como objetos Path y sus ventajas sobre cadenas de texto.
- Crear, modificar y resolver rutas utilizando pathlib de manera portable entre sistemas operativos.
- Realizar operaciones comunes con archivos y directorios: lectura, escritura, creación, eliminación y navegación.
- Utilizar métodos de búsqueda y filtrado de archivos con glob y rglob.
- Comparar pathlib con os.path y entender por qué pathlib es la opción recomendada en Python moderno.