pretty print
nested dictionaries
Python
data formatting
programming tips

How to pretty print nested dictionaries?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Pretty-printing nested dictionaries is useful for debugging APIs, config objects, and intermediate data structures. A plain print(dict_obj) quickly becomes unreadable when nesting depth grows. Python offers multiple options depending on your needs: human-readable console output, stable sorted formatting for diffs, or JSON-compatible serialization.

Choosing the right formatter depends on data type compatibility and whether output is for humans or machines.

Core Sections

1. Use built-in pprint

python
1from pprint import pprint
2
3data = {
4    "user": {"id": 7, "name": "Ari"},
5    "settings": {"theme": "dark", "flags": {"beta": True, "ads": False}},
6}
7
8pprint(data, width=80, sort_dicts=True)

pprint is quick for debugging and supports nested structures well.

2. Use JSON formatting when serializable

python
import json

print(json.dumps(data, indent=2, sort_keys=True))

This is excellent for deterministic output and storing snapshots, but fails for non-JSON types like sets or custom objects.

3. Handle non-serializable objects

python
1def fallback(o):
2    return str(o)
3
4print(json.dumps(data, indent=2, default=fallback))

Fallback keeps formatting robust for mixed Python objects.

4. Build custom recursive printer

python
1def pretty(obj, level=0):
2    pad = "  " * level
3    if isinstance(obj, dict):
4        for k, v in obj.items():
5            print(f"{pad}{k}:")
6            pretty(v, level + 1)
7    else:
8        print(f"{pad}{obj}")

Custom printers help when you need specific formatting constraints.

5. Logging integration

In production logs, pretty output can be noisy. Prefer compact JSON for machines and reserve multiline pretty formatting for debug environments.

Common Pitfalls

  • Using json.dumps on structures containing non-serializable types without fallback handlers.
  • Pretty-printing huge payloads in hot code paths and impacting performance.
  • Assuming key order is meaningful when not using sort_keys or deterministic source maps.
  • Dumping sensitive nested values to logs without redaction.
  • Building custom printers that fail on recursive/self-referential objects.

Summary

For nested dictionaries, pprint is the fastest developer-friendly option, while json.dumps(..., indent=2, sort_keys=True) is best for stable, structured output. Add fallback handling for non-serializable values and redact sensitive keys before logging. With these patterns, nested data becomes readable without sacrificing reliability or security.

A practical way to make this guidance durable is to turn it into an executable runbook instead of leaving it as passive documentation. The runbook should include exact prerequisites, supported versions, required environment variables, and a short verification checklist. Each step should have expected output and one known failure signature so engineers can quickly classify whether they are on the happy path or hitting a known edge case. This structure is especially valuable in parallel team environments where context switches are frequent and not everyone has the same historical knowledge of the system.

It is also useful to keep a minimal reproducible fixture in source control. That fixture can be a small script, test input, sample request, or tiny deployment manifest that demonstrates both success and controlled failure behavior. When dependencies or infrastructure change, this fixture gives a fast signal about compatibility drift. Instead of debugging deep in production workflows, teams can run a focused check in minutes and identify if the regression came from tooling updates, configuration changes, or logic modifications. Reproducible fixtures also improve onboarding by showing the shortest end-to-end path.

For long-term quality, add one lightweight CI guardrail for the most failure-prone step in the workflow. Examples include schema linting, startup smoke checks, deterministic unit tests, API contract assertions, and compatibility probes for key dependencies. Keep guardrails fast and specific so failures are actionable and developers can fix issues without searching logs for long periods. If a class of issue repeats more than once, promote the corresponding manual troubleshooting step into automation. Over time, this shifts effort from reactive firefighting to preventive engineering and keeps the article aligned with real operating conditions.


Course illustration
Course illustration

All Rights Reserved.