En esta página
Archivos y JSON
Trabajando con archivos en Python
La manipulación de archivos es una de las tareas más comunes en Python. El sistema de archivos de cualquier aplicación real involucra leer configuraciones, guardar datos, procesar logs y más.
La función open() y modos de apertura
# Modos de apertura:
# 'r' — lectura (por defecto)
# 'w' — escritura (crea o sobreescribe)
# 'a' — agregar al final (append)
# 'x' — crear exclusivamente (error si ya existe)
# 'b' — modo binario (combina con otros: 'rb', 'wb')
# '+' — lectura y escritura ('r+', 'w+')
# 't' — modo texto (por defecto)
# Forma antigua (no recomendada — hay que recordar close())
archivo = open("hola.txt", "w", encoding="utf-8")
archivo.write("¡Hola, mundo!\n")
archivo.close() # ¿Y si hay una excepción antes?
# Forma moderna con context manager (SIEMPRE usar esto)
with open("hola.txt", "w", encoding="utf-8") as archivo:
archivo.write("¡Hola, mundo!\n")
archivo.write("Esta es la segunda línea.\n")
# El archivo se cierra automáticamente al salir del bloque withLectura de archivos
# Leer todo el contenido de una vez
with open("hola.txt", "r", encoding="utf-8") as f:
contenido = f.read()
print(contenido)
# Leer como lista de líneas
with open("hola.txt", encoding="utf-8") as f:
lineas = f.readlines()
for linea in lineas:
print(linea.rstrip()) # rstrip() elimina el \n al final
# Leer línea a línea (eficiente para archivos grandes)
with open("hola.txt", encoding="utf-8") as f:
for linea in f: # Iteración directa — perezosa
print(linea.strip())
# Leer una sola línea
with open("hola.txt", encoding="utf-8") as f:
primera_linea = f.readline()
segunda_linea = f.readline()Escritura y modos append/write
# Modo 'w' — crea nuevo o sobreescribe
with open("registro.txt", "w", encoding="utf-8") as f:
f.write("Inicio del registro\n")
f.writelines(["Línea 1\n", "Línea 2\n", "Línea 3\n"])
# Modo 'a' — agrega al final sin borrar el contenido existente
from datetime import datetime
def registrar_evento(mensaje: str, archivo: str = "eventos.log") -> None:
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
with open(archivo, "a", encoding="utf-8") as f:
f.write(f"[{timestamp}] {mensaje}\n")
registrar_evento("Usuario Ana inició sesión")
registrar_evento("Usuario Ana actualizó perfil")
registrar_evento("Usuario Ana cerró sesión")
# Verificar existencia antes de leer
import os
if os.path.exists("eventos.log"):
with open("eventos.log", encoding="utf-8") as f:
print(f.read())pathlib.Path — La forma moderna
pathlib ofrece una API orientada a objetos para trabajar con rutas. Es más legible y portátil que os.path:
from pathlib import Path
# Crear rutas
home = Path.home() # Directorio del usuario
actual = Path.cwd() # Directorio actual
config = Path("config") / "app.json" # Concatenación con /
absoluta = Path("/etc/hosts")
# Propiedades de una ruta
ruta = Path("datos/usuarios/ana.json")
print(ruta.name) # ana.json
print(ruta.stem) # ana
print(ruta.suffix) # .json
print(ruta.parent) # datos/usuarios
print(ruta.parents[0]) # datos/usuarios
print(ruta.parents[1]) # datos
print(ruta.is_absolute()) # False
print(ruta.absolute()) # /ruta/completa/datos/usuarios/ana.json
# Crear directorios
(Path("salida") / "reportes").mkdir(parents=True, exist_ok=True)
# Leer y escribir sin open()
archivo = Path("mensaje.txt")
archivo.write_text("¡Hola desde pathlib!\nSegunda línea.", encoding="utf-8")
contenido = archivo.read_text(encoding="utf-8")
print(contenido)
# Archivos binarios
imagen = Path("foto.png")
if imagen.exists():
datos_binarios = imagen.read_bytes()
nueva_imagen = Path("copia_foto.png")
nueva_imagen.write_bytes(datos_binarios)
# Listar archivos y directorios
for elemento in Path(".").iterdir():
tipo = "D" if elemento.is_dir() else "F"
print(f"[{tipo}] {elemento.name}")
# Buscar archivos con glob
for py_file in Path(".").glob("**/*.py"):
print(py_file)
# Información del archivo
if archivo.exists():
stat = archivo.stat()
print(f"Tamaño: {stat.st_size} bytes")
print(f"Modificado: {datetime.fromtimestamp(stat.st_mtime)}")JSON — JavaScript Object Notation
JSON es el formato de intercambio de datos más usado en APIs web. Python incluye el módulo json en la biblioteca estándar:
import json
from datetime import datetime, date
# Tipos Python → JSON
datos = {
"nombre": "Elena Martínez",
"edad": 29,
"activa": True,
"saldo": 1250.75,
"etiquetas": ["admin", "editor"],
"dirección": None,
"preferencias": {
"idioma": "es",
"tema": "oscuro"
}
}
# json.dumps() — Python → string JSON
json_str = json.dumps(datos, ensure_ascii=False, indent=2)
print(json_str)
# json.loads() — string JSON → Python
datos_recuperados = json.loads(json_str)
print(type(datos_recuperados)) # <class 'dict'>
print(datos_recuperados["nombre"]) # Elena Martínez
# json.dump() — Python → archivo
with open("usuario.json", "w", encoding="utf-8") as f:
json.dump(datos, f, ensure_ascii=False, indent=2)
# json.load() — archivo → Python
with open("usuario.json", encoding="utf-8") as f:
datos_del_archivo = json.load(f)Serializar tipos no soportados
JSON no soporta datetime, conjuntos ni objetos personalizados. Puedes crear un encoder personalizado:
class JSONEncoderPersonalizado(json.JSONEncoder):
def default(self, obj: object) -> object:
if isinstance(obj, (datetime, date)):
return obj.isoformat()
if isinstance(obj, set):
return list(obj)
if hasattr(obj, "__dict__"):
return obj.__dict__
return super().default(obj)
datos_complejos = {
"creado": datetime.now(),
"etiquetas": {"python", "backend", "api"},
"version": (3, 14, 0) # Las tuplas sí son soportadas (→ array JSON)
}
json_str = json.dumps(datos_complejos, cls=JSONEncoderPersonalizado,
ensure_ascii=False, indent=2)
print(json_str)
# Una función default más simple
def convertir_json(obj: object) -> object:
if isinstance(obj, (datetime, date)):
return obj.isoformat()
if isinstance(obj, set):
return sorted(obj)
raise TypeError(f"Tipo no serializable: {type(obj)}")
json_str2 = json.dumps(datos_complejos, default=convertir_json, ensure_ascii=False)CSV — Comma-Separated Values
El formato CSV es ideal para datos tabulares (hojas de cálculo, exportaciones de bases de datos):
import csv
from pathlib import Path
# Escribir CSV
empleados = [
{"nombre": "Ana García", "departamento": "Ingeniería", "salario": 75000},
{"nombre": "Bruno López", "departamento": "Ventas", "salario": 55000},
{"nombre": "Carmen Ruiz", "departamento": "Ingeniería", "salario": 80000},
]
ruta_csv = Path("empleados.csv")
with ruta_csv.open("w", newline="", encoding="utf-8") as f:
# DictWriter — escribe dicts como filas
escritor = csv.DictWriter(f, fieldnames=["nombre", "departamento", "salario"])
escritor.writeheader()
escritor.writerows(empleados)
# Leer CSV
with ruta_csv.open(newline="", encoding="utf-8") as f:
lector = csv.DictReader(f)
for fila in lector:
print(f"{fila['nombre']:20} | {fila['departamento']:15} | ${int(fila['salario']):,}")
# CSV simple con listas (sin encabezados de diccionario)
with open("notas.csv", "w", newline="", encoding="utf-8") as f:
escritor = csv.writer(f)
escritor.writerow(["Alumno", "Materia", "Nota"]) # Encabezado manual
escritor.writerows([
["Ana", "Matemáticas", 95],
["Bruno", "Historia", 78],
["Carmen", "Física", 88],
])
with open("notas.csv", newline="", encoding="utf-8") as f:
lector = csv.reader(f)
encabezados = next(lector) # Saltar la primera fila de encabezados
print(encabezados)
for fila in lector:
print(fila)Archivos de texto con formato estructurado
from pathlib import Path
def guardar_tareas(tareas: list[dict], archivo: str = "tareas.txt") -> None:
"""Guarda una lista de tareas en un archivo de texto."""
ruta = Path(archivo)
with ruta.open("w", encoding="utf-8") as f:
f.write(f"# Lista de tareas — {datetime.now().strftime('%Y-%m-%d')}\n\n")
for i, tarea in enumerate(tareas, 1):
estado = "✓" if tarea.get("completada") else "○"
f.write(f"{estado} {i}. {tarea['titulo']}\n")
if tarea.get("descripción"):
f.write(f" {tarea['descripción']}\n")
f.write(f"\nTotal: {len(tareas)} tareas\n")
tareas = [
{"titulo": "Estudiar Python", "completada": True},
{"titulo": "Construir API", "descripción": "Con FastAPI y Pydantic"},
{"titulo": "Escribir tests", "completada": False},
]
guardar_tareas(tareas)
# Leer el archivo generado
contenido = Path("tareas.txt").read_text(encoding="utf-8")
print(contenido)Resumen
Python ofrece herramientas completas para trabajar con archivos: open() con context managers para archivos de texto y binarios, pathlib.Path para manipulación de rutas de forma moderna y portátil, el módulo json para serializar y deserializar datos JSON, y el módulo csv para datos tabulares. En la próxima lección aprenderemos a comunicarnos con APIs HTTP usando requests y httpx.
Inicia sesión para guardar tu progreso