How to compare two dictionaries in Python

Comparing Python dictionaries efficiently helps developers validate data consistency, track state changes, and identify discrepancies between datasets. Python provides multiple built-in methods and operators to compare dictionary contents and structures.

This guide covers essential comparison techniques, practical tips, and real-world applications, with code examples created using Claude, an AI assistant built by Anthropic.

Using the equality operator (==)

dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 2, 'a': 1}
dict3 = {'a': 1, 'b': 3}
print(dict1 == dict2)  # Order doesn't matter
print(dict1 == dict3)  # Values matter
True
False

The equality operator == compares dictionary contents without considering the order of key-value pairs. This behavior makes it particularly useful when validating data consistency across different sources where the insertion sequence might vary.

The example demonstrates two key aspects of dictionary comparison:

  • Order-independence: dict1 and dict2 contain identical key-value pairs in different orders, yet evaluate as equal
  • Value sensitivity: dict1 and dict3 share the same keys but differ in one value, resulting in inequality

Basic comparison techniques

Beyond basic equality checks, Python offers specialized dictionary comparison methods like is, items(), and subset validation to handle more nuanced data validation scenarios.

Comparing dictionary identity with the is operator

dict1 = {'a': 1, 'b': 2}
dict2 = {'a': 1, 'b': 2}
dict3 = dict1
print(dict1 is dict2)  # Different objects with same content
print(dict1 is dict3)  # Same object (reference)
False
True

The is operator checks if two dictionaries reference the exact same object in memory rather than comparing their contents. When you create two separate dictionaries with identical key-value pairs like dict1 and dict2, they occupy different memory locations and return False when compared with is.

However, when you assign dict3 = dict1, both variables point to the same dictionary object in memory. This creates a reference rather than a copy. That's why dict1 is dict3 returns True.

  • Use is to check dictionary identity when you need to verify if two variables reference the same object
  • Use == instead when you want to compare dictionary contents regardless of memory location

Comparing dictionary entries with items()

dict1 = {'a': 1, 'b': 2}
dict2 = {'a': 1, 'c': 3}
common_items = set(dict1.items()) & set(dict2.items())
diff_items = set(dict1.items()) - set(dict2.items())
print(f"Common items: {common_items}")
print(f"Items in dict1 but not in dict2: {diff_items}")
Common items: {('a', 1)}
Items in dict1 but not in dict2: {('b', 2)}

The items() method converts dictionary key-value pairs into a set-compatible format, enabling powerful set operations for comparison. Converting dictionaries to sets unlocks efficient ways to find common elements and differences between them.

  • The & operator identifies shared key-value pairs between dictionaries, stored in common_items
  • The - operator finds key-value pairs unique to the first dictionary, stored in diff_items
  • This approach proves especially useful when comparing complex dictionaries or tracking data changes over time

In the example, both dictionaries share the pair ('a', 1). The pair ('b', 2) exists only in dict1, making it appear in the difference set.

Checking if one dictionary is a subset of another

dict1 = {'a': 1, 'b': 2, 'c': 3}
dict2 = {'a': 1, 'b': 2}
is_subset = all(item in dict1.items() for item in dict2.items())
print(f"dict2 is a subset of dict1: {is_subset}")
print(f"Keys in dict1 not in dict2: {dict1.keys() - dict2.keys()}")
dict2 is a subset of dict1: True
Keys in dict1 not in dict2: {'c'}

The all() function elegantly checks if every key-value pair in dict2 exists within dict1. When all pairs match, we consider dict2 a subset of dict1.

  • The generator expression item in dict1.items() for item in dict2.items() compares each pair efficiently without creating intermediate lists
  • The set operation dict1.keys() - dict2.keys() reveals which keys exist exclusively in dict1
  • This pattern proves invaluable when validating data completeness or verifying that one dictionary contains all required entries from another

In the example, dict2 qualifies as a subset because its key-value pairs 'a': 1 and 'b': 2 exist in dict1. Meanwhile, 'c' appears only in dict1.

Advanced comparison methods

Building on the foundational comparison techniques, Python offers sophisticated methods to handle nested data structures, selective key validation, and flexible numeric comparisons with customizable thresholds.

Deep comparison for nested dictionaries

import json
dict1 = {'a': 1, 'b': {'x': 1, 'y': 2}}
dict2 = {'a': 1, 'b': {'y': 2, 'x': 1}}  # Same content, different order
dict3 = {'b': {'x': 1, 'y': 2}, 'a': 1}  # Same content, different order
print(json.dumps(dict1) == json.dumps(dict2))  # String comparison preserves order
print(dict1 == dict3)  # Dictionary comparison ignores order
False
True

When comparing nested dictionaries, Python's standard equality operator == treats nested structures intelligently. It recursively compares all levels while maintaining order-independence, as shown by dict1 == dict3 returning True.

The json.dumps() method offers a different approach by converting dictionaries to JSON strings. Since string comparison is order-sensitive, json.dumps(dict1) == json.dumps(dict2) returns False despite containing identical data.

  • Use == when you need to compare nested dictionary contents regardless of order
  • Choose json.dumps() comparison when order matters or when you need to verify exact structural matches
  • Both methods handle nested structures automatically without requiring manual recursion

Comparing specific dictionary keys

dict1 = {'name': 'Alice', 'age': 30, 'city': 'New York'}
dict2 = {'name': 'Alice', 'age': 25, 'country': 'USA'}
keys_to_compare = ['name', 'age']
match = all(dict1.get(k) == dict2.get(k) for k in keys_to_compare)
print(f"Dictionaries match on specified keys: {match}")
Dictionaries match on specified keys: False

Selective key comparison lets you validate specific dictionary fields while ignoring others. The get() method safely retrieves values even if a key doesn't exist, returning None instead of raising an error.

  • The keys_to_compare list defines which fields to check—in this case only 'name' and 'age'
  • The all() function with a generator expression efficiently checks if every specified key matches between dictionaries
  • The comparison returns False because while the names match, the ages differ (30 vs 25)

This pattern proves especially useful when comparing data records where only certain fields matter for validation. The different city and country values don't affect the result since they aren't in keys_to_compare.

Custom comparison with tolerance using dictionary comprehension

dict1 = {'a': 10, 'b': 20, 'c': 30}
dict2 = {'a': 11, 'b': 19, 'c': 31}
# Check if values are within ±2 of each other
tolerance_match = all(abs(dict1[k] - dict2[k]) <= 2 for k in dict1 if k in dict2)
print(f"Values match within tolerance: {tolerance_match}")
Values match within tolerance: True

Custom tolerance comparison enables flexible matching between dictionary values that are close but not exactly equal. The abs() function calculates the absolute difference between corresponding values, while the all() function ensures every pair falls within the specified tolerance of ±2.

  • The generator expression abs(dict1[k] - dict2[k]) <= 2 checks if the difference between values is 2 or less
  • The condition k in dict2 safely handles cases where keys exist in one dictionary but not the other
  • This approach proves valuable when comparing numerical data that may have minor variations due to rounding or measurement differences

In the example, all values in dict2 differ from dict1 by only 1, so the comparison returns True. This technique offers more flexibility than strict equality checks when working with real-world data.

Tracking user profile changes with dictionary comparison

Dictionary comparison enables precise tracking of user profile updates by identifying changed fields, modified values, and new or removed attributes in social platforms and content management systems.

old_profile = {'name': 'John', 'bio': 'Python developer', 'followers': 120}
new_profile = {'name': 'John', 'bio': 'Python & JS developer', 'followers': 145}
keys_to_check = set(old_profile) | set(new_profile)
changes = {k: (old_profile.get(k), new_profile.get(k)) for k in keys_to_check 
          if old_profile.get(k) != new_profile.get(k)}
print(f"Profile changes: {changes}")

This code efficiently detects changes between two profile dictionaries. The set() function converts dictionary keys to sets while the | operator combines them into a unified set of all possible keys. A dictionary comprehension then creates changes, which stores only the modified values as tuples.

The get() method safely handles missing keys by returning None instead of raising errors. The comparison runs only when values differ between profiles.

  • The output shows both the old and new values for each changed field
  • The code gracefully handles cases where keys exist in one profile but not the other
  • This approach scales well for dictionaries of any size

Visualizing dictionary differences with the difflib module

Python's difflib module provides a powerful way to visualize differences between dictionaries by converting them into human-readable, line-by-line comparisons that highlight changes, additions, and removals.

import difflib

config1 = {'debug': True, 'api_url': 'api.example.com', 'timeout': 30}
config2 = {'debug': False, 'api_url': 'api.example.com', 'retry': 3}
config1_items = [f"{k}: {v}" for k, v in sorted(config1.items())]
config2_items = [f"{k}: {v}" for k, v in sorted(config2.items())]
diff = difflib.ndiff(config1_items, config2_items)
print('\n'.join(list(diff)))

The code demonstrates a practical way to compare two configuration dictionaries and visualize their differences. The difflib module's ndiff() function analyzes text-based differences between sequences. First, the code converts each dictionary into a sorted list of formatted strings using list comprehension with f"{k}: {v}".

  • The sorted() function ensures consistent ordering of dictionary items
  • The join() method with \n creates a clean, line-by-line output
  • The resulting diff shows added, removed, or modified configuration values with special markers

This approach makes it easy to spot configuration changes at a glance. It's particularly useful when comparing complex dictionaries or tracking changes in configuration files.

Common errors and challenges

Dictionary comparison in Python requires careful handling of missing keys, complex data types, and copy operations to avoid common runtime errors and unexpected behavior.

Avoiding KeyError when comparing dictionaries with missing keys

Direct dictionary key access with square bracket notation raises a KeyError when a key doesn't exist. This common pitfall occurs when comparing dictionaries with different key sets. The code below demonstrates how accessing a missing key interrupts program execution.

def compare_values(dict1, dict2, key):
    return dict1[key] == dict2[key]

user1 = {'name': 'Alice', 'email': 'alice@example.com'}
user2 = {'name': 'Bob', 'phone': '555-1234'}

print(compare_values(user1, user2, 'email'))  # KeyError: 'email'

The compare_values() function directly accesses dictionary keys without checking their existence first. When user2 lacks the 'email' key, Python raises a KeyError. The code below demonstrates a safer approach to handle missing keys.

def compare_values(dict1, dict2, key):
    return key in dict1 and key in dict2 and dict1[key] == dict2[key]

user1 = {'name': 'Alice', 'email': 'alice@example.com'}
user2 = {'name': 'Bob', 'phone': '555-1234'}

print(compare_values(user1, user2, 'email'))  # False

The improved code checks for key existence before attempting comparison. By using the in operator and logical AND (and), the function first verifies that both dictionaries contain the key. Only then does it compare the values.

  • Always verify key existence when comparing dictionaries with potentially different structures
  • The get() method offers an alternative approach by returning None for missing keys
  • This pattern prevents runtime errors in data validation scenarios where dictionary contents may vary

Watch for this issue when working with user input, API responses, or any data source where dictionary structure isn't guaranteed consistent.

Handling TypeError when comparing complex dictionary values

Comparing dictionary values containing different data types like lists and sets can trigger a TypeError. This common issue occurs when applying set operations between incompatible collection types. The code below demonstrates how mixing lists and sets leads to runtime errors.

user1 = {'name': 'Alice', 'tags': ['python', 'data']}
user2 = {'name': 'Bob', 'tags': {'python', 'web'}}

common_tags = user1['tags'] & user2['tags']  # TypeError: unsupported operand
print(f"Common tags: {common_tags}")

The error occurs because the & operator can't directly compare a list and a set. Lists don't support set operations. The code attempts to find common elements between two different collection types. Let's examine the corrected implementation below.

user1 = {'name': 'Alice', 'tags': ['python', 'data']}
user2 = {'name': 'Bob', 'tags': {'python', 'web'}}

common_tags = set(user1['tags']) & set(user2['tags'])
print(f"Common tags: {common_tags}")  # {'python'}

Converting both collections to sets before comparison solves the TypeError. The set() function transforms any iterable into a set, enabling the use of set operations like & to find common elements. This approach works because sets only contain unique values and support efficient intersection operations.

  • Watch for this error when comparing dictionary values containing different collection types
  • Common scenarios include processing user tags, categories, or any nested data structures
  • Always verify the data types of nested collections before applying set operations

The solution proves especially useful when dealing with data from multiple sources where collection types might vary. Converting to sets standardizes the comparison process.

Shallow vs. deep copy confusion in dictionary comparisons

Python's shallow copy mechanism can lead to unexpected behavior when comparing nested dictionaries. The copy() method only creates a surface-level duplicate, leaving nested objects sharing the same memory reference. The code below demonstrates how modifying a nested value affects both the original and copied dictionaries.

original = {'user': {'name': 'Alice', 'score': 85}}
copy = original.copy()  # Shallow copy
copy['user']['score'] = 90

print(original == copy)  # True, but original was modified!
print(original)  # {'user': {'name': 'Alice', 'score': 90}}

The shallow copy creates a new dictionary that still references the same nested user dictionary in memory. When modifying nested values, both dictionaries change because they share that reference. Let's examine the corrected implementation using deep copy.

import copy
original = {'user': {'name': 'Alice', 'score': 85}}
deep_copy = copy.deepcopy(original)  # Deep copy
deep_copy['user']['score'] = 90

print(original == deep_copy)  # False
print(original)  # {'user': {'name': 'Alice', 'score': 85}}

The copy.deepcopy() function creates a completely independent copy of a dictionary and all its nested objects. Unlike shallow copying with .copy(), deep copying ensures that modifying nested values in the copy won't affect the original dictionary.

  • Deep copying recursively duplicates every object in the dictionary hierarchy
  • Each nested dictionary gets its own memory space
  • Changes to nested values remain isolated between copies

Watch for this issue when working with dictionaries containing nested data structures like lists, dictionaries, or custom objects. The problem commonly surfaces in data processing pipelines where you need to preserve the original data while making modifications to a copy.

FAQs

What happens when I use the == operator to compare two dictionaries?

When you use the == operator to compare dictionaries in Python, it checks if both dictionaries contain exactly the same key-value pairs. The comparison returns True only when all keys match and their corresponding values are equal.

The order of items doesn't matter since dictionaries are unordered collections. Python performs this comparison by iterating through each key-value pair and verifying equality. This makes dictionary comparison particularly useful for checking data equivalence in configuration files or API responses.

How can I check if two dictionaries have the same keys but different values?

To compare dictionary keys while ignoring values, use the keys() method with a direct equality check. The set() function converts key collections into sets, enabling a clean comparison that ignores order.

  • The expression set(dict1.keys()) == set(dict2.keys()) returns True when dictionaries share identical keys
  • This approach works because sets only contain unique elements and their order doesn't matter
  • For value comparison, you can use dict1[key] != dict2[key] to identify differences

This method efficiently handles dictionaries of any size while maintaining readable, maintainable code.

Can I compare dictionaries that contain nested lists or other dictionaries?

Yes, Python can directly compare nested dictionaries and lists using the == operator. The comparison works recursively, checking each nested element for equality. Two dictionaries match when they contain identical key-value pairs, regardless of order.

Python performs a deep comparison by default. When you use ==, it traverses through all nested structures to verify that corresponding elements match exactly. This behavior makes it straightforward to compare complex data structures without writing custom comparison logic.

What's the difference between using 'is' and == when comparing dictionaries?

The is operator checks if two dictionaries reference the exact same object in memory, while == compares their contents. Two dictionaries with identical key-value pairs will return True with == but False with is if they're separate objects.

This distinction matters when you need to verify dictionary identity rather than equality. Python creates new dictionary objects even when their contents match. Understanding this behavior helps prevent subtle bugs in reference comparisons.

How do I find which key-value pairs are different between two dictionaries?

Python's dict objects offer several methods to compare dictionaries. The items() method converts dictionary entries into a set-like view, enabling direct comparison. You can find differences using set operations:

  • Use dict1.items() - dict2.items() to find key-value pairs unique to the first dictionary
  • Use dict2.items() - dict1.items() to find pairs unique to the second dictionary

This approach works efficiently because Python optimizes set operations for performance. The resulting sets contain tuples of differing key-value pairs, making it easy to identify exactly what changed between the dictionaries.

🏠