The shelve module provides a persistent dictionary backed by a database. Store any picklable Python object with simple key-value access.

Basic Usage

import shelve
 
# Open (creates files if not exist)
with shelve.open('mydata') as db:
    db['user'] = {'name': 'Alice', 'age': 30}
    db['scores'] = [95, 87, 92]
    db['config'] = {'debug': True}
 
# Later, retrieve data
with shelve.open('mydata') as db:
    print(db['user'])    # {'name': 'Alice', 'age': 30}
    print(db['scores'])  # [95, 87, 92]

Dict-Like Operations

import shelve
 
with shelve.open('mydata') as db:
    # Check existence
    if 'user' in db:
        print("User exists")
    
    # Get with default
    value = db.get('missing', 'default')
    
    # Delete
    del db['old_key']
    
    # Iterate keys
    for key in db:
        print(key, db[key])
    
    # Get all keys
    keys = list(db.keys())

Writeback Mode

By default, modifications to mutable values aren't saved:

import shelve
 
# Problem: mutations not persisted
with shelve.open('mydata') as db:
    db['list'] = [1, 2, 3]
    db['list'].append(4)  # NOT saved!
 
with shelve.open('mydata') as db:
    print(db['list'])  # [1, 2, 3] - append was lost
 
# Solution 1: reassign
with shelve.open('mydata') as db:
    items = db['list']
    items.append(4)
    db['list'] = items  # Explicit reassignment
 
# Solution 2: writeback=True
with shelve.open('mydata', writeback=True) as db:
    db['list'].append(4)  # Automatically tracked
# Changes saved on close

Warning: writeback=True caches everything in memory. Use for small datasets.

Sync and Close

import shelve
 
db = shelve.open('mydata')
try:
    db['key'] = 'value'
    db.sync()  # Force write to disk
    # ... more operations
finally:
    db.close()  # Always close
 
# Better: use context manager
with shelve.open('mydata') as db:
    db['key'] = 'value'
# Auto-synced and closed

What Can Be Stored

Any picklable Python object:

import shelve
from datetime import datetime
from dataclasses import dataclass
 
@dataclass
class User:
    name: str
    email: str
 
with shelve.open('mydata') as db:
    # Primitives
    db['count'] = 42
    db['name'] = 'Alice'
    
    # Collections
    db['items'] = [1, 2, 3]
    db['mapping'] = {'a': 1, 'b': 2}
    
    # Objects
    db['user'] = User('Alice', 'alice@example.com')
    db['timestamp'] = datetime.now()

Not storable: lambdas, open files, sockets, generators.

Practical Examples

Simple Cache

import shelve
import hashlib
 
def cached_fetch(url: str) -> str:
    cache_key = hashlib.md5(url.encode()).hexdigest()
    
    with shelve.open('cache') as cache:
        if cache_key in cache:
            return cache[cache_key]
        
        # Fetch and cache
        import urllib.request
        content = urllib.request.urlopen(url).read().decode()
        cache[cache_key] = content
        return content

Session Storage

import shelve
import uuid
 
class SessionStore:
    def __init__(self, path='sessions'):
        self.path = path
    
    def create(self, data: dict) -> str:
        session_id = str(uuid.uuid4())
        with shelve.open(self.path) as db:
            db[session_id] = data
        return session_id
    
    def get(self, session_id: str) -> dict | None:
        with shelve.open(self.path) as db:
            return db.get(session_id)
    
    def delete(self, session_id: str):
        with shelve.open(self.path) as db:
            if session_id in db:
                del db[session_id]
 
sessions = SessionStore()
sid = sessions.create({'user': 'alice', 'role': 'admin'})
print(sessions.get(sid))

Config Persistence

import shelve
 
class Config:
    def __init__(self, path='config'):
        self.path = path
    
    def get(self, key: str, default=None):
        with shelve.open(self.path) as db:
            return db.get(key, default)
    
    def set(self, key: str, value):
        with shelve.open(self.path) as db:
            db[key] = value
    
    def all(self) -> dict:
        with shelve.open(self.path) as db:
            return dict(db)
 
config = Config()
config.set('theme', 'dark')
config.set('font_size', 14)
print(config.all())

shelve vs Alternatives

Featureshelvesqlite3picklejson
Dict-like API
Query support
Python objectsLimitedLimited
Human readable
Concurrent safe

Use shelve when:

  • Simple key-value storage
  • Storing Python objects
  • Single-process access
  • Quick prototyping

Use sqlite when:

  • Need queries
  • Multiple processes
  • Large datasets
  • Structured data

Limitations

# Keys must be strings
db[123] = 'value'  # Error!
db['123'] = 'value'  # OK
 
# Not thread/process safe without external locking
# Use sqlite3 for concurrent access
 
# Creates multiple files
# mydata.db, mydata.dir, mydata.bak (varies by platform)

Quick Reference

import shelve
 
# Open
db = shelve.open('filename')
db = shelve.open('filename', flag='c')  # Create if needed (default)
db = shelve.open('filename', flag='r')  # Read-only
db = shelve.open('filename', flag='n')  # New (truncate)
db = shelve.open('filename', writeback=True)  # Track mutations
 
# Operations
db['key'] = value   # Store
value = db['key']   # Retrieve
del db['key']       # Delete
'key' in db         # Check
db.keys()           # All keys
db.sync()           # Force write
db.close()          # Close
 
# Context manager (recommended)
with shelve.open('filename') as db:
    db['key'] = value

shelve is the simplest persistence for Python objects. When you outgrow it, move to sqlite3 or a proper database.

React to this post: