Python shelve Module — Persistent Dict-Like Storage

Learn Python shelve for persistent dict-like storage: open(), read/write, access flags, writeback, sync(), thread safety limits, security, and when to use SQLite instead.

Published

Updated

Read time 5 min read

Reviewed byDeepak Prasad

Python shelve Module — Persistent Dict-Like Storage

The shelve module gives you a persistent dictionary: open a file, assign db["key"] = value, and read it back the next time your script runs. It is built on pickle and dbm, so you get Python-native storage without SQL — useful for caches, small app state, and local tooling.

The examples below follow the official shelve documentation; run them locally with Python 3.12+ to confirm behavior on your machine.

Tested on: Python 3.12+ (standard library); examples match the official shelve documentation.


Quick answer: store and read data with shelve

python
import shelve

with shelve.open("my_shelf") as db:
    db["name"] = "John"
    db["age"] = 30

with shelve.open("my_shelf") as db:
    print(db["name"], db["age"])
Output

This creates shelf files in the current working directory (typically my_shelf.db plus index files, depending on the dbm backend). Keys must be strings.


shelve vs JSON, CSV, pickle, and SQLite

Option Best for Limitation
shelve Quick local persistence, dict workflow String keys only; not for untrusted data; poor concurrency
JSON Human-readable, language-neutral exchange No arbitrary Python types (sets, custom classes)
CSV Tabular exports Flat rows only — see read and write CSV in Python
pickle Serialize one blob to a file No dict-like incremental keys
SQLite Queries, relations, many readers/writers More setup than shelve

Use shelve when you want dictionary ergonomics on a single machine. Move to SQLite when you need SQL, integrity constraints, or safer multi-process access.


Open a shelf and perform basic operations

A shelf behaves like a Python dictionary for most day-to-day use. Pick a path the same way you would for write to file in Python — use a dedicated folder if the shelf will grow.

python
import shelve

with shelve.open("my_shelf") as db:
    db["name"] = "John"
    db["age"] = 30
    db["active"] = True
Output

Read values with subscript or get():

python
with shelve.open("my_shelf") as db:
    name = db["name"]
    age = db.get("age", 0)
    missing = db.get("missing_key", "default")
Output

List keys, values, or items when you need to inspect the store:

python
with shelve.open("my_shelf") as db:
    print(list(db.keys()))
    print(list(db.values()))
    print(list(db.items()))
Output

Keys, values, and custom objects

Keys must be strings. Integer keys raise TypeError:

python
with shelve.open("my_shelf") as db:
    db["ok_key"] = 123

with shelve.open("my_shelf") as db:
    db[123] = "fails"  # TypeError: keys must be strings
Output

Values can be lists, nested dicts, or custom objects (picklable):

python
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

with shelve.open("my_shelf") as db:
    db["people"] = [{"name": "John", "age": 30}]
    db["john"] = Person("John", 30)
Output

Access flags: read-only, create, and exclusive create

Flag Meaning
'r' Read-only — existing shelf must exist
'c' Read/write — create if missing (default)
'n' Create a new empty shelf — error if files already exist
'w' Read/write — create if missing, but truncate if format is unrecognized

Read-only example:

python
import shelve

with shelve.open("my_shelf", flag="r") as db:
    print(db.get("name"))
Output

Exclusive create for a fresh database file:

python
with shelve.open("new_shelf", flag="n") as db:
    db["first"] = "run only"
Output

If the shelf path does not exist and you pass flag='r', shelve.open raises an error — which is what you want for read-only tooling.


writeback, sync(), and the mutable-value gotcha

By default, in-place mutation of a value does not persist:

python
with shelve.open("my_shelf") as db:
    db["tags"] = ["a", "b"]
    db["tags"].append("c")  # change is lost when the shelf closes
Output

Either reassign the key or enable writeback=True:

python
with shelve.open("my_shelf", writeback=True) as db:
    if "tags" not in db:
        db["tags"] = []
    db["tags"].append("c")
    db.sync()  # flush writeback cache to disk
Output

sync() writes cached data without closing the shelf. close() (or exiting a with block) also flushes. With writeback=True, shelve keeps accessed entries in memory — fine for small shelves, costly for huge ones.


Thread safety and concurrent access

shelve is not thread-safe and does not support safe concurrent writes across processes. For threads, guard access with a threading.Lock:

python
import shelve
import threading

lock = threading.Lock()

def write_key(key, value):
    with lock:
        with shelve.open("my_shelf") as db:
            db[key] = value

def read_key(key, default=None):
    with lock:
        with shelve.open("my_shelf") as db:
            return db.get(key, default)
Output

For multi-process or high-concurrency workloads, use SQLite, Redis, or another database instead of shelve.


Security and error handling

WARNING
shelve uses pickle. Loading a shelf file from an untrusted source can execute arbitrary code. Treat shelf paths like executable data — only open files you created or fully trust.

Wrap I/O in try / except in Python for permission errors, missing files, and corrupted databases:

python
import shelve

try:
    with shelve.open("my_shelf") as db:
        db["key"] = "value"
except OSError as exc:
    print(f"Could not access shelf: {exc}")
Output

Validate keys (strings only) before write to avoid TypeError in long-running jobs.


Advanced: subclassing Shelf

You can extend shelve.Shelf for custom behavior — for example, storing expiry metadata alongside values. Keep the implementation picklable and document that subclasses are for advanced use only.

python
import shelve
import time

class TimestampShelf(shelve.Shelf):
    def __setitem__(self, key, value):
        super().__setitem__(key, (time.time(), value))

    def __getitem__(self, key):
        _ts, value = super().__getitem__(key)
        return value
Output

Most apps never need this — flags, writeback, and sync() cover typical scripts.


When to choose something else

Reach for JSON or CSV when humans or other languages must read the file. Use SQLite when you need:

  • Concurrent readers/writers with transactions
  • Indexed lookup and SQL reporting
  • Safer handling of partially trusted inputs (still parameterize queries)

shelve remains a strong choice for single-user tools, local caches, and prototypes where a dict API is enough.


Summary

shelve.open() exposes a dict-like store backed by disk files. Use string keys, pick access flags deliberately, and understand writeback before mutating lists or dicts in place. Do not use shelve for security-sensitive or highly concurrent production paths — use SQLite or a dedicated database instead.


References


Frequently Asked Questions

1. What is the Python shelve module?

shelve is a standard-library module that stores picklable Python objects on disk behind a dict-like interface, so you can persist data between program runs without running a database server.

2. Do I need to pip install shelve?

No. shelve ships with Python. Import it with import shelve after you install Python itself.

3. What key types does shelve allow?

Keys must be strings. Values can be any object that pickle supports, including lists, dicts, and custom class instances.

4. When should I use writeback=True?

Use writeback when you modify mutable values in place, such as appending to a list stored in the shelf. Call sync() before exit or rely on close() to flush cached writes.

5. Is shelve safe for untrusted data?

No. shelve uses pickle under the hood. Never open shelf files from untrusted sources, because malicious pickle data can execute arbitrary code during load.

6. What should I use instead of shelve for production apps?

Use SQLite for structured queries, JSON for portable text storage, or a real database when you need concurrency, transactions, or multi-user access.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …