mrkeyoor.com_
Sun 20 Sept 11:42 UTC
PyPIUtilsupdated 20 Sept 2026

deepdiff review

DeepDiff 9.1.0 walks two Python object graphs and groups each mismatch by kind and path: changed values, added keys, type changes, moved iterable items, or repetition counts. It understands dictionaries, sequences, sets, named tuples, dates, decimals, and user objects rather than limiting input to JSON. The distribution also contains DeepHash for content-based hashes, DeepSearch for nested lookup, Extract for following DeepDiff paths, and Delta for replaying changes. The current release adds eligible multiprocessing work, wildcard path filters, a new cache, typing corrections, and an early block on dunder traversal in Delta paths.

Verdict

DeepDiff 9.1.0 installed in 0.3 seconds and used 2 MB in our sandbox, with a working 0.70-second import and 0 audit findings. Use it for explainable Python object differences; use JSON Patch for cross-language changes and a focused assertion helper for tiny tests.

We installed it

Lab card: what happened when we installed deepdiffScreenshot of deepdiff documentation
Install✓ · 0.3s3 packages on disk · 2 MB
Importimport deepdiff in 0.70s · pure Python · py.typed · requires Python >=3.10
Known vulns0(pip-audit)

Answers from our run

Does deepdiff install cleanly?

Yes. In a fresh container with an empty cache, pip install deepdiff finished in 0.3s, leaving 3 packages and 2 MB on disk. pip-audit reported no known vulnerabilities.

What does deepdiff need to run?

Python >=3.10, and nothing compiled: it is pure Python. In our run import deepdiff succeeded in 0.70s, and the package ships py.typed for type checkers.

deepdiff or jsonpatch: which should you use?

jsonpatch: Choose it when another language must consume or apply standard RFC 6902 JSON operations. DeepDiff 9.1.0 installed in 0.3 seconds and used 2 MB in our sandbox, with a working 0.70-second import and 0 audit findings.

When should you not use deepdiff?

Changes cross a service boundary as JSON; RFC 6902 operations from jsonpatch are language-neutral, while DeepDiff Delta is tied to Python behavior

API stability3/5DeepDiff(left, right, ...) and its category-keyed result remain the central interface in version 9.1.0. Code that parses root-style paths, serializes tree views, or stores Delta bytes has a wider compatibility surface. Version 9 removed view_override from to_dict and to_json, changed tree-view defaults, and dropped Python 3.9. Ordinary comparisons migrate more easily than stored or presentation-oriented output, which supports a score of 3.
Docs4/5The versioned site gives DeepDiff, DeepHash, DeepSearch, Extract, Delta, and the CLI their own pages, backed by examples and detailed parameter references. Release 9.1.0 documents its multiprocessing fallbacks and new glob syntax in the README. Interactions among ignore_order, repetition reporting, distance cutoffs, cache settings, and custom operators still require representative test data rather than a quick-start example, so the documentation earns 4 instead of 5.
Maintenance4/5GitHub shows an unarchived repository last pushed on 2026-08-04, with 106 open issues and pull requests. The 9.1.0 notes cover multiprocessing, a cache rewrite, packaging repairs, several Delta correctness fixes, and immediate rejection of dunder traversal. That is meaningful maintenance rather than release-only churn. The remaining queue and the breadth of supported Python objects make pinned upgrades with saved fixtures prudent.
Ecosystem4/5The package recorded 18,272,991 downloads for the latest week, and GitHub reports 2,524 stars. DeepHash, DeepSearch, Extract, Delta, and the optional CLI cover adjacent tasks without changing imports, while extras connect serialization to orjson and common data formats. Interoperability stops at Python-specific paths and Delta representation, so heterogeneous systems still need JSON Patch or another agreed wire format.

Use it if

  • A failed test must show the precise path and old and new values inside a nested Python object
  • Sequence order carries no meaning and matching elements by their content is worth extra CPU time
  • Comparison rules need numeric tolerance, ignored paths, custom operators, or selected type equivalence
  • One Python codebase needs recursive comparisons together with content hashing, nested search, or replayable deltas
Skip it if

Setup reality

We installed DeepDiff 9.1.0 without a cache in a fresh Python 3.12 Bookworm sandbox. Installation took 0.3 seconds and left 3 packages totaling 2 MB. The lab metadata counted 33 direct dependencies, and pip-audit reported 0 known vulnerabilities. This is a pure-Python package requiring Python 3.10 or newer, with an MIT license and a py.typed marker. Importing deepdiff succeeded in 0.70 seconds.

The base package asks for no account, token, or project file. Install deepdiff[cli] for its shell command and deepdiff[optimize] for optional orjson serialization. YAML, TOML, CSV, and Pydantic handling each depend on separate packages. Pin the extras the application actually calls; installing deepdiff alone does not promise every loader shown in the documentation.

Version 9.1 path filters use strings such as root['orders'][0]['created_at']. Glob support accepts an unquoted star segment in include_paths and exclude_paths. A syntactically valid path may match nothing, so keep a fixture containing one known ignored change. Regex filters match the rendered path string, which makes quoting and brackets part of the rule.

Setting ignore_order=True replaces positional comparison with content matching. Large repeated collections may need cache and cutoff tuning. Version 9.1 can parallelize distance work and subtree comparisons, but custom operators, object callbacks, and ignore_order_func trigger a serial fallback. Treat serialized Delta bytes as package-specific Python data, and never load a Delta supplied by an untrusted party.

Patterns

Locate changes inside nested objects basic-diff

from deepdiff import DeepDiff

t1 = {"a": 1, "b": [1, 2, 3], "c": {"d": "x"}}
t2 = {"a": 2, "b": [1, 3, 2], "c": {"d": "X"}, "e": 9}

DeepDiff(t1, t2)
# {'dictionary_item_added': ["root['e']"],
#  'values_changed': {"root['a']": {'new_value': 2, 'old_value': 1},
#                     "root['b'][1]": {'new_value': 3, 'old_value': 2},
#                     "root['b'][2]": {'new_value': 2, 'old_value': 3},
#                     "root['c']['d']": {'new_value': 'X', 'old_value': 'x'}}}

The result is falsy when no category contains a difference. Lists use their positions unless you select unordered matching.

Match list members by content ignore-order

from deepdiff import DeepDiff

DeepDiff({"b": [1, 2, 3]}, {"b": [1, 3, 2]}, ignore_order=True)
# {}

DeepDiff([1, 1, 2], [1, 2], ignore_order=True, report_repetition=True)
# {'repetition_change': {'root[0]': {'old_repeat': 2, 'new_repeat': 1,
#                                    'old_indexes': [0, 1], 'new_indexes': [0], 'value': 1}}}

ignore_order performs element pairing instead of index comparison. report_repetition preserves a mismatch in duplicate counts.

Omit volatile object paths exclude-paths

from deepdiff import DeepDiff

t1 = {"a": {"ts": 1, "v": 2}, "b": {"ts": 1, "v": 2}}
t2 = {"a": {"ts": 9, "v": 2}, "b": {"ts": 9, "v": 3}}

DeepDiff(t1, t2, exclude_paths=["root['a']['ts']", "root['b']['ts']"])
# {'values_changed': {"root['b']['v']": {'new_value': 3, 'old_value': 2}}}

DeepDiff(t1, t2, exclude_paths=["root[*]['ts']"])   # wildcard, added in 9.1
# {'values_changed': {"root['b']['v']": {'new_value': 3, 'old_value': 2}}}

Version 9.1 recognizes an unquoted star as a glob segment. Keep a fixture that proves the rule hides the intended timestamp.

Limit the walk to selected branches include-and-regex-paths

from deepdiff import DeepDiff

DeepDiff(t1, t2, include_paths=["root['b']"])
# only differences under b

DeepDiff(t1, t2, exclude_regex_paths=[r"\['ts'\]"])
# every key named ts, at any depth

include_paths narrows traversal to named branches. exclude_regex_paths runs against DeepDiff's rendered root path.

Treat nearby numbers as equal numeric-tolerance

from deepdiff import DeepDiff

DeepDiff({"p": 1.0001}, {"p": 1.0002}, significant_digits=3)   # {}
DeepDiff({"p": 1.0}, {"p": 1.0001}, math_epsilon=0.001)        # {}

significant_digits compares rounded representations, whereas math_epsilon sets an absolute gap. They encode different domain rules.

Choose which numeric types are equivalent ignore-type-changes

from deepdiff import DeepDiff

DeepDiff({"n": 1}, {"n": 1.0})
# {'type_changes': {"root['n']": {'old_type': int, 'new_type': float,
#                                 'old_value': 1, 'new_value': 1.0}}}

DeepDiff({"n": 1}, {"n": 1.0}, ignore_numeric_type_changes=True)   # {}
DeepDiff({"n": 1}, {"n": "1"}, ignore_type_in_groups=[(int, str)])
# {'values_changed': {"root['n']": {'new_value': '1', 'old_value': 1}}}

A permitted type group suppresses the type mismatch only. Different values can still appear under values_changed.

Serialize or summarize a result render-the-diff

from deepdiff import DeepDiff

diff = DeepDiff(t1, t2)

diff.to_json()
# '{"dictionary_item_added": ["root[\'e\']"], "values_changed": {...}}'

print(diff.pretty())
# Item root['e'] added to dictionary.
# Value of root['a'] changed from 1 to 2.

diff.affected_root_keys   # SetOrdered(['a', 'b', 'c', 'e'])

to_json needs a conversion for values outside JSON's data model. affected_root_keys is a smaller signal for routing downstream work.

Replay and reverse a recorded change delta-patch

from deepdiff import DeepDiff, Delta

delta = Delta(DeepDiff({"a": 1}, {"a": 2}))
{"a": 1} + delta          # {'a': 2}

reversible = Delta(DeepDiff({"a": 1}, {"a": 2}), bidirectional=True)
{"a": 2} - reversible     # {'a': 1}

blob = delta.dumps()      # bytes, for storage or transport
Delta(blob) is not None

Subtraction works only for a bidirectional Delta. Serialized Delta bytes can drive object traversal, so reject data from untrusted sources.

Hash an object by its contents hash-nested-content

from deepdiff import DeepHash

record = {"tags": {"blue", "green"}, "meta": {"active": True}}
hashes = DeepHash(record)
content_hash = hashes[record]

DeepHash handles nested Python values that built-in hash rejects. Changing a nested member produces a different digest.

Find text in keys and values search-nested-values

from deepdiff import DeepSearch

config = {"database": {"host": "db.internal"}, "owner": "ops"}
result = DeepSearch(config, "internal", case_sensitive=False)
print(result)

DeepSearch reports matched paths in categorized sets. It searches the Python object graph rather than querying an external index.

Read a value from a reported path extract-by-path

from deepdiff import extract

order = {"lines": [{"sku": "A1"}, {"sku": "B2"}]}
value = extract(order, "root['lines'][1]['sku']")
assert value == "B2"

extract follows DeepDiff's root path syntax. Do not pass an attacker-controlled path into an object that exposes sensitive attributes.

Compare record lists by identity match-records-by-id

from deepdiff import DeepDiff

before = [{"id": 1, "name": "Ada"}, {"id": 2, "name": "Lin"}]
after = [{"id": 2, "name": "Linus"}, {"id": 1, "name": "Ada"}]

diff = DeepDiff(before, after, group_by="id")

group_by turns each list into records indexed by the selected field. Duplicate or missing identifiers make that comparison unsuitable.

Alternatives

PackageRegistryPick it when
jsonpatchPyPIChoose it when another language must consume or apply standard RFC 6902 JSON operations.
dictdifferPyPIChoose it for a smaller dictionary and sequence differ that can patch and revert its tuples.
jsondiffPyPIChoose it when inputs stay JSON-shaped and a compact diff syntax is sufficient.

More utils guides

lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.