El módulo shelve permite guardar objetos Python en un archivo persistente con una interfaz parecida a un diccionario. Es útil cuando un script necesita conservar una pequeña cantidad de estado entre ejecuciones sin instalar un servidor de base de datos. Su sencillez no elimina los riesgos relacionados con seguridad, portabilidad, concurrencia y mantenimiento.
Cuándo conviene usar shelve
Utiliza shelve en scripts locales, prototipos, utilidades de línea de comandos, cachés pequeñas, automatizaciones personales y herramientas de escritorio. Puedes abrir una estantería, asignar valores a claves de texto y cerrarla. Para aplicaciones web, varios procesos, consultas complejas o datos críticos, es mejor elegir SQLite, PostgreSQL u otra base transaccional.
Ejemplo básico
import shelve
with shelve.open("datos_app") as db:
db["usuario"] = {"nombre": "Ana", "nivel": 3}
db["tema"] = "oscuro"
with shelve.open("datos_app") as db:
print(db["usuario"])
El bloque with garantiza el cierre incluso si ocurre una excepción. Según el sistema operativo y la implementación dbm, una sola base lógica puede producir varios archivos con extensiones diferentes. Considera el nombre enviado a open como una base y no como la garantía de un único archivo.
Funcionamiento interno
shelve combina una base clave-valor de la familia dbm con la serialización de objetos mediante pickle. Las claves son cadenas. Los valores pueden ser listas, diccionarios, tuplas, instancias de clases y otros objetos serializables. Esta flexibilidad es cómoda, pero los datos no son legibles como JSON y nunca deben cargarse desde una fuente no confiable.
Actualización de valores mutables
Un error frecuente consiste en recuperar una lista o un diccionario, modificarlo y esperar que el cambio se guarde automáticamente. El patrón más seguro es volver a asignar el objeto modificado.
with shelve.open("datos_app") as db:
perfil = db["usuario"]
perfil["nivel"] += 1
db["usuario"] = perfil
La opción writeback=True mantiene en caché los objetos consultados y los escribe al cerrar. Puede simplificar el código, pero aumenta el consumo de memoria y puede hacer que el cierre tarde mucho.
with shelve.open("datos_app", writeback=True) as db:
db["usuario"]["nivel"] += 1
Usa writeback solamente con conjuntos pequeños y después de comprender su coste. La reasignación explícita suele ser más predecible.
Operaciones disponibles
Una estantería admite métodos conocidos como keys(), values(), items(), get() y pop(), además del operador in. Recorrer todos los valores puede ser costoso porque cada elemento necesita deserialización.
with shelve.open("datos_app") as db:
db["contador"] = db.get("contador", 0) + 1
if "configuracion" in db:
print(db["configuracion"])
for clave in db.keys():
print(clave)
Modos de apertura
El argumento flag controla la apertura. El valor predeterminado c permite lectura y escritura y crea la base cuando no existe. r abre una base existente en modo de solo lectura. w abre una base existente para leer y escribir. n crea una base vacía y reemplaza la anterior.
Prefiere r en procesos que solo consultan información. Usa n únicamente para reconstrucciones deliberadas, pruebas aisladas o comandos de reinicio.
Reglas de seguridad
Nunca abras una estantería enviada por un usuario, descargada de internet, recibida por correo o modificada por una cuenta no confiable. Los datos pickle pueden ejecutar código durante la carga. Mantén los archivos en un directorio controlado, con permisos restrictivos y fuera de carpetas públicas.
shelve tampoco cifra los valores. Contraseñas, tokens, claves privadas y credenciales de API deben guardarse en un gestor de secretos o en otro sistema protegido.
Concurrencia e integridad
El módulo no ofrece un bloqueo portátil para varios escritores. Dos procesos escribiendo en la misma base pueden corromperla. La regla segura es permitir un solo escritor. Si varios procesos o máquinas necesitan compartir los datos, utiliza SQLite con transacciones o un servicio de base de datos diseñado para concurrencia.
Un cierre brusco o una pérdida de energía también puede dejar archivos inconsistentes. Realiza copias de seguridad, prueba la restauración y considera escribir cambios en una base temporal antes de sustituir la principal.
Portabilidad y migraciones
La implementación dbm cambia entre sistemas operativos e instalaciones. Los archivos creados en un equipo pueden no abrirse en otro. Los objetos pickle dependen además de rutas de módulos y definiciones de clases compatibles. Renombrar o mover una clase puede impedir la lectura de datos antiguos.
Para información que debe durar años, viajar entre plataformas o ser consumida por otros lenguajes, usa JSON, CSV, SQLite o un esquema documentado. shelve funciona mejor como persistencia local controlada.
Un repositorio sencillo
from pathlib import Path
import shelve
class Repositorio:
def __init__(self, ruta: Path):
self.ruta = str(ruta)
def guardar(self, clave: str, valor) -> None:
with shelve.open(self.ruta) as db:
db[clave] = valor
def obtener(self, clave: str, predeterminado=None):
with shelve.open(self.ruta) as db:
return db.get(clave, predeterminado)
def eliminar(self, clave: str) -> bool:
with shelve.open(self.ruta) as db:
if clave not in db:
return False
del db[clave]
return True
Encapsular el almacenamiento centraliza rutas, validación, registros y copias de seguridad. También facilita una futura migración a SQLite, porque el resto de la aplicación depende de tu interfaz y no de llamadas dispersas a shelve.
Pruebas seguras
En las pruebas, utiliza tempfile.TemporaryDirectory para obtener almacenamiento aislado y limpieza automática.
from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory() as carpeta:
repo = Repositorio(Path(carpeta) / "prueba")
repo.guardar("x", {"valor": 10})
assert repo.obtener("x")["valor"] == 10
Buenas prácticas operativas
Usa claves estables y documentadas. Valida valores antes de escribir. Cierra siempre con with. Evita writeback para colecciones grandes. Mantén un único escritor. Añade copias de seguridad para datos valiosos. Incluye una exportación a JSON y comprueba que puede restaurarse en otra máquina.
Alternativas
JSON es mejor para datos simples y portables. SQLite aporta transacciones, consultas, índices y acceso concurrente más seguro. dbm guarda bytes y deja la serialización a la aplicación. SQLModel y SQLAlchemy son opciones cuando aumentan el dominio y las consultas.
Conclusión
shelve es un puente práctico entre un diccionario temporal y una base completa. Reduce código en proyectos locales pequeños, pero no debe convertirse en una dependencia crítica invisible. Úsalo solo con archivos confiables, comprende los riesgos de pickle, limita la concurrencia y planifica una migración cuando la durabilidad o la escala sean importantes.
Continúa con zipfile en Python, tempfile en Python, tomllib en Python y copy en Python. Consulta la documentación de shelve y la advertencia de seguridad de pickle.
Antes de adoptar el módulo, documenta qué datos se guardarán, durante cuánto tiempo y cómo se recuperarán ante un fallo. Esta decisión evita que un prototipo termine convertido en una dependencia de producción sin supervisión.







