Archivos y E/S
Leer y escribir archivos es una tarea diaria: registros, configuraciones, cargas y exportaciones. pathlib.Path proporciona una API de rutas orientada a objetos, mientras que los gestores de contexto garantizan que los descriptores se cierren incluso cuando ocurren errores.
Receta
from pathlib import Path
path = Path("data/config.json")
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text('{"debug": true}', encoding="utf-8")
raw = path.read_bytes()
text = raw.decode("utf-8")Cuándo usar esto:
- Cargar configuraciones JSON/YAML/TOML al inicio
- Escribir registros de auditoría y exportaciones CSV
- Transmitir archivos grandes sin cargar todos los bytes
- Actualizaciones atómicas de configuración mediante archivo temporal + renombrado
Ejemplo de Trabajo
import json
import tempfile
from pathlib import Path
def atomic_write_json(path: Path, payload: dict) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
with tempfile.NamedTemporaryFile(
mode="w",
encoding="utf-8",
dir=path.parent,
delete=False,
suffix=".tmp",
) as tmp:
json.dump(payload, tmp, indent=2)
tmp_path = Path(tmp.name)
tmp_path.replace(path)
def tail_lines(path: Path, max_lines: int = 50) -> list[str]:
if not path.exists():
return []
lines = path.read_text(encoding="utf-8").splitlines()
return lines[-max_lines:]
def copy_binary(src: Path, dst: Path, chunk_size: int = 1024 * 64) -> None:
dst.parent.mkdir(parents=True, exist_ok=True)
with src.open("rb") as rf, dst.open("wb") as wf:
while chunk := rf.read(chunk_size):
wf.write(chunk)
if __name__ == "__main__":
cfg = Path("data/settings.json")
atomic_write_json(cfg, {"retries": 3, "timeout": 30})
print(tail_lines(Path("data/app.log")))Lo que esto demuestra:
- Escritura atómica mediante archivo temporal en el directorio de destino y luego
Path.replace - Las lecturas de texto siempre pasan
encoding="utf-8" - La copia binaria transmite fragmentos: memoria limitada para archivos grandes
mkdir(parents=True, exist_ok=True)crea directorios anidados de forma segura
Inmersión Profunda
Cómo Funciona
- Texto vs binario -
"r"/"w"devuelvenstr;"rb"/"wb"devuelvenbytes. - Gestores de contexto -
with open(...) as fllama af.close()enfinally. - pathlib - Envuelve
os/opencon métodos dePath(read_text,glob,iterdir). - Codificaciones - UTF-8 por defecto para texto multiplataforma; especifica la política de errores (
strict,replace). - Descriptores de archivo - El sistema operativo limita los archivos abiertos; siempre ciérralos o usa
with.
Operaciones Comunes de Rutas
| Tarea | pathlib |
|---|---|
| Unir rutas | base / "child" / "file.txt" |
| Comprobar existencia | path.exists() |
| Glob | path.glob("**/*.py") |
| Resolver | Ruta absoluta path.resolve() |
Notas de Python
from contextlib import ExitStack
with ExitStack() as stack:
a = stack.enter_context(path_a.open())
b = stack.enter_context(path_b.open())
# ambos se cierran al salirErrores Comunes
- Codificación faltante - El valor predeterminado de la plataforma puede no ser UTF-8 en Windows. Solución:
encoding="utf-8"siempre para texto. - Leer archivos enormes con read_text - Carga el archivo completo en RAM. Solución: Iterar líneas o leer fragmentos binarios.
- Concatenación de cadenas de rutas -
"dir" + "/file"falla en Windows. Solución:Path("dir") / "file". - Carreras TOCTOU - Comprobar
exists()y luego abrir; otro proceso puede eliminarlo. Solución:try/except FileNotFoundError. - Olvidar la nueva línea al escribir - Los registros se juntan. Solución: Escribir
line + "\n"oprint(..., file=fh).
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
pathlib | La mayoría de las tareas de archivo | Necesita flags de os.open de nivel más bajo |
aiofiles | Cargas asíncronas de FastAPI | Script CLI síncrono |
tempfile | Escrituras atómicas, temporales | Rutas de almacenamiento permanentes |
| Almacenamiento de objetos (S3) | Activos compartidos duraderos | Solo configuraciones de desarrollo local |
Preguntas Frecuentes
¿pathlib u os.path?
Prefiere pathlib para código nuevo: operadores legibles y ayudantes de lectura/escritura de alto nivel.
¿Cómo añado contenido a un archivo de registro?
with path.open("a", encoding="utf-8") as fh: fh.write(line + "\n")
¿Qué modo para imágenes binarias?
"rb" y "wb" - nunca decodifiques imágenes como texto UTF-8.
¿Cómo listo archivos recursivamente?
for p in Path("src").rglob("*.py"): print(p)
¿Qué es el reemplazo atómico?
Escribe un temporal en el mismo directorio del sistema de archivos, luego Path.replace - los lectores ven el archivo antiguo o el nuevo, nunca uno parcial.
¿Cómo manejo archivos que no existen?
Captura FileNotFoundError o comprueba path.exists() cuando la configuración opcional es aceptable.
¿Puede pathlib leer JSON directamente?
No - lee el texto y luego json.loads. O usa json.load con un manejador de archivo abierto.
¿Qué política de errores de codificación debo usar?
strict para pipelines validados; replace o backslashreplace solo para ingesta de registros con pérdida.
¿Cómo obtengo el tamaño del archivo?
path.stat().st_size sin leer el contenido.
¿El `with` se cierra ante una excepción?
Sí - el protocolo del gestor de contexto ejecuta __exit__ y cierra el archivo incluso cuando un error se propaga.
Relacionado
- Cadenas y f-strings - codificar/decodificar texto
- Gestores de Contexto - patrones
withavanzados - arrays, bytes y memoryview - búferes binarios
- Inicio Rápido de Entornos Virtuales - rutas de proyecto
Versiones de Stack: Esta página fue escrita para Python 3.14.0 (estable 3.14, mantenimiento 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, y uv 0.6+.