The shelve module offers a lightweight approach to serializing and storing arbitrary Python objects directly onto the filesystem. Upon initialization, it automatically generates a trio of system files sharing a common prefix but distinguished by .bak, .dat, and .dir extensions, handling metadata, binary payloads, and index mapping respectively.
Fundamental Operations
Accessing the storage engine mirrors standard dictionary interactions. Utilizing context managers ensures proper resource deallocation and guarantees buffered data is flushed before execution terminates.
import shelve
storage_name = "product_catalog"
# Writing records
with shelve.open(storage_name) as catalog:
catalog["sku_001"] = {"item": "Mechanical Keyboard", "stock": 42}
catalog["sku_002"] = "Wireless Mouse"
catalog["inventory_tags"] = ["electronics", "peripherals"]
# Reading and iterating
with shelve.open(storage_name) as catalog:
print("Registered Keys:", list(catalog.keys()))
for identifier, payload in catalog.items():
print(f"[{identifier}] {payload}")
if "sku_001" in catalog:
print("SKU lookup successful.")
Handling Mutable Types and Persistence Gaps
A criticla behavior to understand involves mutable containers like lists and dictionaries. The module extracts deep copies when retrieving values. Consequently, in-place modifications to these extracted objects do not automatically propagate back to the underlying database.
import shelve
log_store = "service_metrics"
# Initial population
with shelve.open(log_store) as metrics:
metrics["traffic"] = {"requests": 100, "errors": 0}
# Attempting in-place mutation (fails silently)
with shelve.open(log_store) as metrics:
snapshot = metrics["traffic"]
snapshot["requests"] += 50
print("File reflects original value:", metrics["traffic"]["requests"])
# Correct manual synchronization approach
with shelve.open(log_store) as metrics:
current_state = metrics["traffic"]
current_state["requests"] += 50
metrics["traffic"] = current_state # Explicit reassignment triggers storage
Alternatively, passing writeback=True during initialization intercepts attribute mutations and queues updates for later synchronization. While this syntax simplifies workflows, it introduces significant overhead. The engine loads all persisted objects into memory upon opening and must traverse the entire cache during the close operation to commit changes, leading to higher RAM consumption and slower shutdown times.
import shelve
with shelve.open(log_store, writeback=True) as metrics:
metrics["traffic"]["requests"] += 10
print("Direct mutation succeeds:", metrics["traffic"]["requests"])