Skip to content

Python pprint: Make Nested Data Actually Readable

The Problem

I was debugging an API response the other day, and I got this:

messy_output.py
response = {
"users": [
{"id": 1, "name": "Alice", "roles": ["admin", "editor"], "settings": {"theme": "dark", "notifications": True}},
{"id": 2, "name": "Bob", "roles": ["viewer"], "settings": {"theme": "light", "notifications": False}}
],
"metadata": {"total": 2, "page": 1, "per_page": 10}
}
print(response)

The output was a nightmare:

{'users': [{'id': 1, 'name': 'Alice', 'roles': ['admin', 'editor'], 'settings': {'theme': 'dark', 'notifications': True}}, {'id': 2, 'name': 'Bob', 'roles': ['viewer'], 'settings': {'theme': 'light', 'notifications': False}}], 'metadata': {'total': 2, 'page': 1, 'per_page': 10}}

One long line. No indentation. Impossible to read. I had to copy it to an online formatter just to understand the structure.

Then I remembered: Python has a built-in module for this.

The Solution: pprint

Python’s pprint module is designed exactly for this problem. It formats complex data structures with proper indentation and line breaks.

pprint_basic.py
from pprint import pprint
response = {
"users": [
{"id": 1, "name": "Alice", "roles": ["admin", "editor"], "settings": {"theme": "dark", "notifications": True}},
{"id": 2, "name": "Bob", "roles": ["viewer"], "settings": {"theme": "light", "notifications": False}}
],
"metadata": {"total": 2, "page": 1, "per_page": 10}
}
pprint(response)

Now the output looks like this:

{'metadata': {'page': 1, 'per_page': 10, 'total': 2},
'users': [{'id': 1,
'name': 'Alice',
'roles': ['admin', 'editor'],
'settings': {'notifications': True, 'theme': 'dark'}},
{'id': 2,
'name': 'Bob',
'roles': ['viewer'],
'settings': {'notifications': False, 'theme': 'light'}}]}

Suddenly I can see the structure. Each level is indented. Dictionary keys are sorted alphabetically. Lists break across lines when they get long.

When to Use pprint vs print

I reach for pprint when I’m dealing with:

  • API responses (JSON converted to dicts)
  • Configuration files loaded into Python
  • Database query results
  • Nested data structures in general
  • Debugging complex objects

Regular print() is fine for simple things:

simple_print.py
# print() is fine for simple output
name = "Alice"
count = 42
print(f"User {name} has {count} items") # Perfectly readable

But once I have nested structures, print() fails me:

nested_print.py
# print() struggles with nested data
config = {
"database": {
"host": "localhost",
"port": 5432,
"credentials": {"user": "admin", "password": "secret"}
},
"cache": {
"backend": "redis",
"servers": ["redis1", "redis2", "redis3"]
}
}
print(config) # One line mess
pprint(config) # Readable structure

Getting the Formatted String: pformat()

Sometimes I don’t want to print directly. I want to capture the formatted string for logging or further processing.

pprint.pprint() prints to stdout, but pprint.pformat() returns a string:

pformat_example.py
from pprint import pformat
config = {
"database": {
"host": "localhost",
"port": 5432,
"credentials": {"user": "admin", "password": "secret"}
}
}
# Returns a string instead of printing
formatted = pformat(config)
# Now I can use it however I want
log_message = f"Loaded config:\n{formatted}"
print(log_message)
# Or save to file
with open("config_dump.txt", "w") as f:
f.write(formatted)

This is useful when:

  • Logging to a file
  • Building error messages
  • Storing debug output
  • Testing (comparing formatted strings)

Controlling the Output Width

By default, pprint tries to fit things in 80 characters. I can change that:

width_control.py
from pprint import pprint
data = {"items": ["apple", "banana", "cherry", "date", "elderberry"]}
# Default width (80 characters)
pprint(data)
# {'items': ['apple', 'banana', 'cherry', 'date', 'elderberry']}
# Narrow width - forces more line breaks
pprint(data, width=40)
# {'items': ['apple',
# 'banana',
# 'cherry',
# 'date',
# 'elderberry']}
# Wide width - fits more on one line
pprint(data, width=120)
# {'items': ['apple', 'banana', 'cherry', 'date', 'elderberry']}

The width parameter controls when pprint decides to wrap lines.

Controlling Indentation and Depth

When I have deeply nested data, I can control how deep to go:

depth_control.py
from pprint import pprint
deeply_nested = {
"level1": {
"level2": {
"level3": {
"level4": {
"value": "deep"
}
}
}
}
}
# Limit depth to 2 levels
pprint(deeply_nested, depth=2)
# {'level1': {'level2': {...}}}

The {...} shows that pprint stopped at that depth. This is helpful when I only care about the top-level structure.

I can also control indentation:

indent_control.py
from pprint import pprint
data = {"users": [{"name": "Alice"}, {"name": "Bob"}]}
# Default indent (1 space)
pprint(data)
# {'users': [{'name': 'Alice'}, {'name': 'Bob'}]}
# Custom indent (4 spaces)
pprint(data, indent=4)
# { 'users': [ {'name': 'Alice'},
# {'name': 'Bob'}]}

A Real Debugging Workflow

Here’s how I use pprint in actual debugging:

debug_workflow.py
import json
from pprint import pprint
# 1. Load some data
response = '{"status": "ok", "data": {"users": [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}], "count": 2}}'
data = json.loads(response)
# 2. Quick check - what's the structure?
pprint(data)
# {'data': {'count': 2, 'users': [{'id': 1, 'name': 'Alice'}, {'id': 2, 'name': 'Bob'}]}, 'status': 'ok'}
# 3. Something's wrong - let me check a specific part
pprint(data["data"]["users"])
# [{'id': 1, 'name': 'Alice'}, {'id': 2, 'name': 'Bob'}]
# 4. Need more detail on one item?
pprint(data["data"]["users"][0], width=40)
# {'id': 1, 'name': 'Alice'}

The key insight: I use pprint as a quick “what does this look like?” tool. No need to copy-paste into formatters or add print statements with manual formatting.

pprint vs json.dumps for Formatting

I used to use json.dumps with indent for formatting:

json_dumps.py
import json
data = {"name": "Alice", "items": [1, 2, 3]}
# Using json.dumps
print(json.dumps(data, indent=2))
# {
# "name": "Alice",
# "items": [
# 1,
# 2,
# 3
# ]
# }

But json.dumps has limitations:

  • Only works with JSON-serializable types (no sets, tuples, custom objects)
  • Changes types (tuples become lists)
  • Fails on things like datetime objects
pprint_vs_json.py
from pprint import pprint
import json
data = {
"items": {1, 2, 3}, # Set - not JSON serializable
"coords": (10, 20), # Tuple
"name": "test"
}
# json.dumps fails
# json.dumps(data) # TypeError: Object of type set is not JSON serializable
# pprint handles it fine
pprint(data)
# {'coords': (10, 20), 'items': {1, 2, 3}, 'name': 'test'}

I use pprint for debugging Python objects and json.dumps when I specifically need JSON output.

Summary

Python’s pprint module solves the “unreadable nested data” problem with zero dependencies:

  • pprint(data) prints formatted output to stdout
  • pformat(data) returns the formatted string
  • width controls line length
  • depth limits how deep to format
  • indent controls indentation spacing

The next time I’m debugging an API response or complex data structure, I reach for pprint instead of struggling with one-liner print() output. It’s built into Python, requires no installation, and saves me from copy-pasting into online formatters.

quick_reference.py
from pprint import pprint, pformat
# Quick print
pprint(data)
# Get as string
formatted = pformat(data)
# Control formatting
pprint(data, width=60, indent=2, depth=3)

One import, instant readability.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments