How to save a dictionary in Python
Learn how to save a Python dictionary with various methods. Explore tips, real-world uses, and debug common errors for efficient data storage.

To persist data, you must save your Python dictionary. This lets you store and retrieve complex data structures, ensuring application state or configurations are not lost between sessions.
In this article, we'll explore techniques from simple text files to binary formats like pickle. You'll also get practical tips, see real-world applications, and receive debugging advice for common issues.
Using json to save a dictionary
import json
my_dict = {'name': 'John', 'age': 30, 'city': 'New York'}
with open('data.json', 'w') as f:
json.dump(my_dict, f)
# Reading back the dictionary
with open('data.json', 'r') as f:
loaded_dict = json.load(f)
print(loaded_dict)--OUTPUT--{'name': 'John', 'age': 30, 'city': 'New York'}
The json module is a standard for data persistence because it converts Python objects into a human-readable text format. This portability is a major advantage, as it allows other applications and services to easily consume your data, regardless of the programming language they use.
The process is straightforward. You use json.dump() to serialize the dictionary and write it to a file. Later, json.load() reads that file and deserializes the data back into a Python dictionary, effectively restoring its state.
Basic serialization methods
While json is excellent for its portability, Python’s standard library offers other specialized tools for different serialization tasks and data structures.
Using the pickle module for object serialization
import pickle
my_dict = {'name': 'John', 'age': 30, 'city': 'New York'}
with open('data.pkl', 'wb') as f:
pickle.dump(my_dict, f)
# Reading back the dictionary
with open('data.pkl', 'rb') as f:
loaded_dict = pickle.load(f)
print(loaded_dict)--OUTPUT--{'name': 'John', 'age': 30, 'city': 'New York'}
The pickle module is Python's native tool for object serialization. Unlike json, it converts your dictionary into a binary format. This format isn't human-readable, but it's highly efficient and can handle a much wider range of Python data types.
- Process: You use
pickle.dump()to write the object to a file opened in binary write mode ('wb'). To retrieve it, you usepickle.load()with a file opened in binary read mode ('rb'). - Security Warning: It's important to note that the
picklemodule is not secure. You should only unpickle data from sources you trust completely.
Creating persistent dictionary with shelve
import shelve
my_dict = {'name': 'John', 'age': 30, 'city': 'New York'}
with shelve.open('data_shelf') as shelf:
shelf['user'] = my_dict
# Reading back the dictionary
with shelve.open('data_shelf') as shelf:
loaded_dict = shelf['user']
print(loaded_dict)--OUTPUT--{'name': 'John', 'age': 30, 'city': 'New York'}
The shelve module provides a persistent, dictionary-like object that acts as a simple on-disk database. This is different from loading an entire file into memory—you interact with the "shelf" directly.
- Process: Use
shelve.open()to create or access the shelf file. You can then assign objects to keys, such asshelf['user'] = my_dict, and retrieve them the same way. - Advantage: Its main benefit is allowing you to work with individual objects by key without loading the entire dataset, which is efficient for larger collections.
Writing flat dictionaries to CSV files
import csv
my_dict = {'name': 'John', 'age': 30, 'city': 'New York'}
with open('data.csv', 'w', newline='') as f:
writer = csv.writer(f)
for key, value in my_dict.items():
writer.writerow([key, value])
# Display file content
with open('data.csv', 'r') as f:
print(f.read())--OUTPUT--name,John
age,30
city,New York
The csv module is a great choice for saving flat dictionaries, especially if you plan to open the data in a spreadsheet application. This approach is most effective for simple key-value structures without nested data.
- Process: You create a
csv.writerobject and iterate through the dictionary's items. Thewriter.writerow()function then writes each key-value pair as a separate line in the file. - Note: Using
newline=''when opening the file is a crucial detail that prevents unwanted blank rows from appearing in your CSV output.
Advanced serialization methods
As your data needs grow, you can move beyond basic file I/O to more robust methods for handling complex structures and improving storage efficiency.
Saving dictionaries with YAML format
import yaml
my_dict = {'name': 'John', 'age': 30, 'city': 'New York', 'skills': ['Python', 'SQL']}
with open('data.yaml', 'w') as f:
yaml.dump(my_dict, f, default_flow_style=False)
# Reading back the dictionary
with open('data.yaml', 'r') as f:
loaded_dict = yaml.safe_load(f)
print(loaded_dict)--OUTPUT--{'name': 'John', 'age': 30, 'city': 'New York', 'skills': ['Python', 'SQL']}
YAML is a human-friendly data format that’s often preferred over JSON for its cleaner syntax, especially in configuration files. It handles complex, nested data structures with greater readability.
- Process: Using the third-party PyYAML library, you can serialize a dictionary with
yaml.dump(). The argumentdefault_flow_style=Falseis used to create a more readable, block-style output instead of a compact, inline one. - Security: For deserialization, it’s best practice to use
yaml.safe_load(). This function safely loads the data while preventing the execution of arbitrary code that could be present in a malicious file.
Compressing dictionaries while saving
import json
import gzip
import os
my_dict = {'name': 'John', 'age': 30, 'city': 'New York', 'bio': 'A' * 1000}
# Save normal and compressed versions
with open('data_large.json', 'w') as f:
json.dump(my_dict, f)
with gzip.open('data_large.json.gz', 'wt') as f:
json.dump(my_dict, f)
print(f"Regular size: {os.path.getsize('data_large.json')} bytes")
print(f"Compressed size: {os.path.getsize('data_large.json.gz')} bytes")--OUTPUT--Regular size: 1057 bytes
Compressed size: 58 bytes
When working with large dictionaries, compressing the data as you save it can significantly reduce file size. This is especially useful for saving disk space or speeding up network transfers. Python’s built-in gzip module integrates smoothly with serialization libraries like json.
- Process: Instead of a standard
open()call, you usegzip.open()with the mode set to write text ('wt'). This creates a file-like object that automatically compresses any data written to it. - Benefit: You can then pass this object directly to
json.dump(). As the example shows, the resulting compressed file is a fraction of the original size.
Storing dictionaries in SQLite database
import sqlite3
import json
my_dict = {'name': 'John', 'age': 30, 'city': 'New York'}
conn = sqlite3.connect(':memory:') # In-memory database for demonstration
c = conn.cursor()
c.execute('CREATE TABLE data (id INTEGER PRIMARY KEY, dict_data TEXT)')
c.execute('INSERT INTO data (dict_data) VALUES (?)', (json.dumps(my_dict),))
# Retrieving the stored dictionary
c.execute('SELECT dict_data FROM data')
loaded_dict = json.loads(c.fetchone()[0])
print(loaded_dict)--OUTPUT--{'name': 'John', 'age': 30, 'city': 'New York'}
For more structured data persistence, you can use a database like SQLite, which is built into Python. Since a database can't store a dictionary object directly, you first convert it into a string. This is where the json module becomes incredibly useful.
- Process: You serialize the dictionary into a JSON string using
json.dumps(). This string is then stored in aTEXTcolumn in your SQLite table. - Retrieval: To get the dictionary back, you simply query the database for the string and use
json.loads()to parse it back into a Python dictionary.
Move faster with Replit
Replit is an AI-powered development platform that comes with all Python dependencies pre-installed, so you can skip setup and start coding instantly. With Agent 4, you can move from learning individual techniques to building complete apps from a simple description.
Instead of piecing together code, you can describe the app you want to build and let the Agent handle the rest. For example, you could create:
- A configuration manager that saves user settings into a human-readable YAML file.
- A lightweight inventory tool that stores product dictionaries as JSON strings in an SQLite database.
- A data utility that compresses large log files with
gzipbefore saving them to disk.
Simply describe your app, and Replit will write the code, test it, and fix issues automatically, all within your browser.
Common errors and challenges
Even with the right tools, you might run into a few common roadblocks when saving and loading your dictionary data.
Handling non-serializable types with json
The json module can't natively handle every Python type. If your dictionary contains objects like dates, times, or custom classes, attempting to save it with json.dump() will raise a TypeError because JSON only understands a limited set of data types.
- The Problem: You have a dictionary with a non-standard object, such as one from the
datetimemodule, and need to serialize it. - The Solution: You can provide a custom function to the
defaultparameter ofjson.dump(). This function tells the serializer how to convert the unsupported object into a JSON-compatible format, like an ISO-formatted string.
Handling file not found errors when loading dictionaries
A FileNotFoundError is a frequent issue, especially the first time an application runs or if a data file is moved or deleted. Trying to load a dictionary from a non-existent file will cause your program to crash if the error isn't handled.
- The Problem: Your code tries to open and read a file that isn't there yet.
- The Solution: Wrap your file-loading logic in a
try...except FileNotFoundErrorblock. In theexceptpart, you can gracefully handle the situation by creating a default dictionary or logging a message.
Handling JSONDecodeError when parsing invalid JSON
Sometimes a file exists, but its contents are not valid JSON. This can happen if a file write was interrupted, the file is empty, or it was manually edited incorrectly. When json.load() tries to parse this corrupted data, it will raise a JSONDecodeError.
- The Problem: The data file is corrupted or empty, preventing successful parsing.
- The Solution: Use a
try...except json.JSONDecodeErrorblock around yourjson.load()call. This allows you to catch the error and decide what to do next—perhaps you'll load a default configuration or notify the user of the corruption.
Handling non-serializable types with json
The json module can't serialize every Python type out of the box. You'll encounter a TypeError if your dictionary contains complex objects, like a datetime instance, which JSON doesn't natively understand. The code below shows what happens when you try.
import json
from datetime import datetime
# Dictionary with a datetime object (not JSON serializable)
user_data = {
'name': 'John',
'joined_date': datetime.now()
}
# This will raise a TypeError
with open('user.json', 'w') as f:
json.dump(user_data, f)
The json.dump() function fails because it doesn't know how to convert the datetime object into a string, which triggers the TypeError. The following code demonstrates how to resolve this issue.
import json
from datetime import datetime
user_data = {
'name': 'John',
'joined_date': datetime.now().isoformat() # Convert to string
}
# Now it works
with open('user.json', 'w') as f:
json.dump(user_data, f)
The fix is to manually convert any non-serializable objects into a JSON-friendly format before saving. In this case, calling .isoformat() on the datetime object turns it into a standard string. This simple pre-processing step ensures json.dump() can handle the data without raising a TypeError. You'll want to apply this same logic whenever your dictionary contains custom classes, dates, or other complex objects that aren't native to JSON.
Handling file not found errors when loading dictionaries
A FileNotFoundError is a common hurdle, especially when an application runs for the first time and a configuration file hasn't been created. If you try to load a dictionary from a non-existent file, your program will crash. The code below demonstrates this exact scenario.
import json
# This will raise a FileNotFoundError if config.json doesn't exist
with open('config.json', 'r') as f:
config = json.load(f)
print(f"Theme: {config.get('theme')}")
The open() function, when used in read mode ('r'), requires the file to exist. Since config.json hasn't been created, the program halts with an error. The code below shows how to handle this gracefully.
import json
import os
# Check if file exists before loading
if os.path.exists('config.json'):
with open('config.json', 'r') as f:
config = json.load(f)
else:
# Use default config if file doesn't exist
config = {'theme': 'light', 'font_size': 12}
print(f"Theme: {config.get('theme')}")
The solution is to proactively check if the file exists before attempting to read it. By using os.path.exists(), you can verify the file's presence. If it's there, you load it as usual; if not, you create a default dictionary. This approach is crucial for applications that rely on configuration files, ensuring they can initialize correctly on the first run or if the file gets deleted, preventing an unexpected crash.
Handling JSONDecodeError when parsing invalid JSON
A JSONDecodeError pops up when json.loads() tries to parse a file that isn’t valid JSON. This often happens if a file write was interrupted or it was edited incorrectly, leaving the data corrupted. The following code demonstrates this exact scenario.
import json
# Malformed JSON string (missing quotes around keys)
invalid_json = '{"name": "John", age: 30}'
# This will raise json.JSONDecodeError
parsed_data = json.loads(invalid_json)
The json.loads() function fails because the key age isn't enclosed in quotes, which is a strict requirement for valid JSON. This triggers the error. The code below shows how to handle this potential crash.
import json
# Malformed JSON string (missing quotes around keys)
invalid_json = '{"name": "John", age: 30}'
try:
parsed_data = json.loads(invalid_json)
except json.JSONDecodeError as e:
print(f"Error parsing JSON: {e}")
# Use a default value or fix the JSON
parsed_data = {"name": "John", "age": 30}
print(parsed_data)
The solution is to wrap the json.loads() call in a try...except block. This lets you catch a json.JSONDecodeError if the data is malformed. In the except block, you can handle the error gracefully. For instance, you can log the issue and load a default dictionary to prevent your application from crashing. This is crucial when reading files that might be empty or corrupted by an incomplete write operation.
Real-world applications
Beyond just avoiding errors, these persistence methods are the building blocks for many practical features you'll find in everyday software and vibe coding projects.
Managing application settings with json
You can use the json module to save a dictionary of settings, allowing your application to remember user preferences like theme or font size between sessions.
import json
# Application settings dictionary
app_config = {
'theme': 'dark',
'font_size': 12,
'auto_save': True,
'recent_files': ['doc1.txt', 'project.py']
}
# Save configuration
with open('app_settings.json', 'w') as f:
json.dump(app_config, f, indent=2)
# Load the configuration
with open('app_settings.json', 'r') as f:
loaded_config = json.load(f)
print(loaded_config)
This example shows a practical way to manage application settings. The app_config dictionary holds all your configuration data, from theme choices to a list of recent files.
- The
json.dump()function writes this dictionary to a file, usingindent=2to format the JSON for easy reading. - Later,
json.load()reads that file and reconstructs the dictionary in memory.
This process lets you save and retrieve complex settings with just a few lines of code, ensuring your application's state can be easily preserved.
Implementing a simple cache using json and timestamps
To avoid making redundant API calls, you can save a dictionary with a timestamp, creating a simple cache that lets you check if the data is fresh enough to use or needs to be fetched again.
import json
import time
# Create weather data with current timestamp
weather_data = {
'weather': 'sunny',
'temperature': 75,
'timestamp': time.time()
}
# Save to cache file
with open('weather_cache.json', 'w') as f:
json.dump(weather_data, f)
# Read from cache and check age
with open('weather_cache.json', 'r') as f:
cached_data = json.load(f)
cache_age = time.time() - cached_data['timestamp']
print(f"Cache age: {cache_age:.2f} seconds")
print(f"Weather: {cached_data['weather']}, {cached_data['temperature']}°F")
This example demonstrates a simple caching mechanism. It begins by creating a dictionary, weather_data, and uses time.time() to embed the current Unix timestamp. This marks exactly when the data was captured before it's saved to a JSON file.
- The script first writes the dictionary to
weather_cache.jsonusingjson.dump(). - It then immediately reads the data back into a new variable,
cached_data, withjson.load(). - Finally, it calculates the
cache_ageby subtracting the stored timestamp from the current time, effectively measuring how old the cached data is.
Get started with Replit
Put your knowledge into practice. Describe your tool to Replit Agent, like: 'Build a utility that exports user profiles to a CSV file' or 'Create a simple cache that saves API data to a JSON file'.
The Agent handles writing the code, testing for bugs, and deploying your app from a single prompt. Start building with Replit.
Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.
Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.



