The shelve module provides a simple way to persist Python objects. It's like a dictionary that automatically saves to disk—perfect for quick local storage.
Basic Usage
Store and retrieve objects:
import shelve
# Open a shelf (creates files like data.db)
with shelve.open('mydata') as db:
db['name'] = 'Alice'
db['scores'] = [95, 87, 92]
db['config'] = {'debug': True, 'version': 1}
# Data persists across runs
with shelve.open('mydata') as db:
print(db['name']) # Alice
print(db['scores']) # [95, 87, 92]Dictionary Interface
Shelves behave like dictionaries:
import shelve
with shelve.open('mydata') as db:
# Set values
db['key'] = 'value'
# Get values
value = db['key']
value = db.get('missing', 'default')
# Check existence
if 'key' in db:
print('exists')
# Delete
del db['key']
# Iterate
for key in db:
print(key, db[key])
# Get all keys
print(list(db.keys()))Storing Complex Objects
Any picklable Python object works:
import shelve
from datetime import datetime
class User:
def __init__(self, name, email):
self.name = name
self.email = email
self.created = datetime.now()
with shelve.open('users') as db:
db['alice'] = User('Alice', 'alice@example.com')
db['bob'] = User('Bob', 'bob@example.com')
# Later...
with shelve.open('users') as db:
user = db['alice']
print(f"{user.name}: {user.email}")Writeback Mode
By default, modifying retrieved objects doesn't persist:
import shelve
# Without writeback - changes are LOST
with shelve.open('mydata') as db:
db['items'] = [1, 2, 3]
with shelve.open('mydata') as db:
db['items'].append(4) # This doesn't persist!
with shelve.open('mydata') as db:
print(db['items']) # [1, 2, 3] - append was lost
# With writeback - changes persist
with shelve.open('mydata', writeback=True) as db:
db['items'].append(4) # This persists!
with shelve.open('mydata') as db:
print(db['items']) # [1, 2, 3, 4]Writeback Trade-offs
import shelve
# writeback=True:
# + Modifications to objects persist
# + More intuitive behavior
# - Uses more memory (caches all accessed items)
# - Slower close() (writes all cached items)
with shelve.open('mydata', writeback=True) as db:
# All accessed items cached until close
for key in db:
db[key]['modified'] = True # Works!Manual Sync
Force write to disk:
import shelve
db = shelve.open('mydata', writeback=True)
db['key'] = 'value'
db.sync() # Write to disk now
# ... more operations ...
db.close()Read-Only Mode
Open for reading only:
import shelve
with shelve.open('mydata', flag='r') as db:
print(db['key'])
# db['new'] = 'value' # Error! Read-onlyFlags
import shelve
# 'c' - Create if doesn't exist (default)
# 'n' - Always create new (truncate existing)
# 'r' - Read-only
# 'w' - Read-write (error if doesn't exist)
# Fresh start
with shelve.open('mydata', flag='n') as db:
db['fresh'] = 'data'Simple Cache
import shelve
import hashlib
import time
def cached_computation(key, compute_func, ttl=3600):
"""Cache computation results to disk."""
cache_key = hashlib.md5(key.encode()).hexdigest()
with shelve.open('cache') as cache:
if cache_key in cache:
entry = cache[cache_key]
if time.time() - entry['time'] < ttl:
return entry['value']
# Compute and cache
value = compute_func()
cache[cache_key] = {'value': value, 'time': time.time()}
return valueSession Storage
import shelve
import uuid
class SessionStore:
def __init__(self, path='sessions'):
self.path = path
def create(self, data):
"""Create new session."""
session_id = str(uuid.uuid4())
with shelve.open(self.path) as db:
db[session_id] = data
return session_id
def get(self, session_id):
"""Get session data."""
with shelve.open(self.path) as db:
return db.get(session_id)
def update(self, session_id, data):
"""Update session."""
with shelve.open(self.path) as db:
db[session_id] = data
def delete(self, session_id):
"""Delete session."""
with shelve.open(self.path) as db:
if session_id in db:
del db[session_id]Application Config
import shelve
class Config:
def __init__(self, path='config'):
self.path = path
self._defaults = {
'debug': False,
'log_level': 'INFO',
'max_connections': 10
}
def get(self, key):
with shelve.open(self.path) as db:
return db.get(key, self._defaults.get(key))
def set(self, key, value):
with shelve.open(self.path) as db:
db[key] = value
def reset(self):
with shelve.open(self.path, flag='n') as db:
for key, value in self._defaults.items():
db[key] = valueFile Location
Shelve creates database files:
import shelve
import os
with shelve.open('mydata') as db:
db['test'] = 'data'
# Creates files like:
# mydata.db (or mydata.dir, mydata.bak, mydata.dat)
# Exact files depend on underlying dbm implementationLimitations
- Keys must be strings: Unlike dict, shelve requires string keys
- Not thread-safe: Use locking for concurrent access
- Not suitable for large data: Better to use SQLite or dedicated DB
- Pickling required: Objects must be picklable
import shelve
with shelve.open('mydata') as db:
# db[123] = 'value' # Error! Key must be string
db['123'] = 'value' # OKThread Safety
Add locking for concurrent access:
import shelve
import threading
class ThreadSafeShelf:
def __init__(self, path):
self.path = path
self.lock = threading.Lock()
def get(self, key):
with self.lock:
with shelve.open(self.path) as db:
return db.get(key)
def set(self, key, value):
with self.lock:
with shelve.open(self.path) as db:
db[key] = valueWhen to Use shelve
Use shelve when:
- Quick prototype persistence
- Simple key-value storage
- Caching Python objects locally
- Small to medium data sizes
Use alternatives when:
- Need concurrent access → SQLite
- Large datasets → SQLite, Redis
- Need queries → SQLite
- Cross-language compatibility → JSON, SQLite
Shelve is the quickest path from "I need to save this" to working code.
React to this post: