The pprint module formats complex data structures for human readability. Essential for debugging, logging, and understanding nested data.
Basic Pretty Printing
from pprint import pprint
data = {
'users': [
{'name': 'Alice', 'age': 30, 'roles': ['admin', 'editor']},
{'name': 'Bob', 'age': 25, 'roles': ['viewer']},
],
'metadata': {'version': '1.0', 'created': '2024-01-01'}
}
# Standard print (hard to read)
print(data)
# {'users': [{'name': 'Alice', 'age': 30, 'roles': ['admin', 'editor']}, ...
# Pretty print (readable)
pprint(data)
# {'metadata': {'created': '2024-01-01', 'version': '1.0'},
# 'users': [{'age': 30, 'name': 'Alice', 'roles': ['admin', 'editor']},
# {'age': 25, 'name': 'Bob', 'roles': ['viewer']}]}Controlling Width
from pprint import pprint
data = {'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}
# Default width (80)
pprint(data)
# {'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}
# Narrow width (forces wrapping)
pprint(data, width=40)
# {'key1': 'value1',
# 'key2': 'value2',
# 'key3': 'value3'}
# Very narrow
pprint(data, width=1)
# {'key1': 'value1',
# 'key2': 'value2',
# 'key3': 'value3'}Controlling Depth
from pprint import pprint
nested = {
'level1': {
'level2': {
'level3': {
'level4': 'deep value'
}
}
}
}
# Show only 2 levels deep
pprint(nested, depth=2)
# {'level1': {'level2': {...}}}
# Full depth (default)
pprint(nested)
# {'level1': {'level2': {'level3': {'level4': 'deep value'}}}}Indentation
from pprint import pprint
data = {'a': [1, 2, 3], 'b': [4, 5, 6]}
# Default indent (1)
pprint(data, width=20)
# {'a': [1, 2, 3],
# 'b': [4, 5, 6]}
# Larger indent
pprint(data, width=20, indent=4)
# {'a': [1, 2, 3],
# 'b': [4, 5, 6]}Getting String Output
from pprint import pformat
data = {'name': 'Alice', 'scores': [95, 87, 92]}
# Get formatted string (don't print)
formatted = pformat(data)
print(type(formatted)) # <class 'str'>
# Use in logging
import logging
logging.info("Data: %s", pformat(data))
# Use in f-strings
message = f"Received:\n{pformat(data)}"PrettyPrinter Class
Reusable formatter with custom settings:
from pprint import PrettyPrinter
# Create configured printer
pp = PrettyPrinter(indent=2, width=60, depth=3)
data = {'key': 'value', 'nested': {'a': 1, 'b': 2}}
pp.pprint(data)
formatted = pp.pformat(data)Compact Mode (Python 3.8+)
Fit more items per line:
from pprint import pprint
items = list(range(20))
# Default (one per line when it doesn't fit)
pprint(items, width=40)
# [0,
# 1,
# 2,
# ...
# Compact (squeeze multiple per line)
pprint(items, width=40, compact=True)
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
# 12, 13, 14, 15, 16, 17, 18, 19]Sorting Keys
from pprint import pprint
data = {'z': 1, 'a': 2, 'm': 3}
# Default: sorted keys (Python 3.8+)
pprint(data)
# {'a': 2, 'm': 3, 'z': 1}
# Preserve insertion order
pprint(data, sort_dicts=False)
# {'z': 1, 'a': 2, 'm': 3}Debugging with pprint
from pprint import pprint
def process_api_response(response):
# Debug: see what we got
print("Response received:")
pprint(response, depth=2)
# Process...
return response['data']
# In debugger or REPL
response = {'data': {'users': [...], 'meta': {...}}}
pprint(response)Custom Objects
from pprint import pprint
class User:
def __init__(self, name, email):
self.name = name
self.email = email
def __repr__(self):
return f"User(name={self.name!r}, email={self.email!r})"
users = [User('Alice', 'alice@example.com'), User('Bob', 'bob@example.com')]
pprint(users)
# [User(name='Alice', email='alice@example.com'),
# User(name='Bob', email='bob@example.com')]Logging Integration
from pprint import pformat
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
def log_response(response: dict):
"""Log API response in readable format."""
logger.debug("API Response:\n%s", pformat(response))
# Usage
log_response({'status': 'ok', 'data': {'items': [1, 2, 3]}})File Output
from pprint import pprint
data = {'config': {'debug': True, 'options': ['a', 'b', 'c']}}
# Write to file
with open('debug_output.txt', 'w') as f:
pprint(data, stream=f)
# Or use pformat
with open('debug_output.txt', 'w') as f:
f.write(pformat(data))Comparing with JSON
import json
from pprint import pformat
data = {'users': [{'name': 'Alice'}, {'name': 'Bob'}]}
# JSON (no trailing commas, double quotes)
print(json.dumps(data, indent=2))
# {
# "users": [
# {"name": "Alice"},
# {"name": "Bob"}
# ]
# }
# pprint (Python syntax, single quotes)
print(pformat(data))
# {'users': [{'name': 'Alice'}, {'name': 'Bob'}]}Practical Patterns
Config Debugging
from pprint import pformat
def load_config(path: str) -> dict:
config = {'loaded': True, 'settings': {}} # Load from file
print(f"Loaded config from {path}:\n{pformat(config)}")
return configTest Output
from pprint import pformat
def test_api_response():
response = fetch_api()
assert 'data' in response, f"Missing 'data' in:\n{pformat(response)}"REPL Helper
# In your Python REPL config (~/.pythonrc.py)
from pprint import pprint as pp
# Now use pp() for quick inspection
# >>> pp(some_complex_dict)When to Use pprint
| Scenario | Tool |
|---|---|
| Debug output | pprint() |
| Log files | pformat() |
| JSON API response | json.dumps(indent=2) |
| Machine processing | repr() or json.dumps() |
| User display | Custom formatting |
The pprint module is your debugging companion for complex data. Use it whenever you need to understand what's actually in your nested structures.
React to this post: