How to compare two dictionaries in Python
Learn how to compare two dictionaries in Python. Discover different methods, tips, real-world applications, and how to debug common errors.

Dictionary comparison in Python is a frequent task for developers. You can check for equality with the == operator or identify differences between key-value pairs with various methods.
In this article, you'll learn several techniques to compare dictionaries. You'll also find practical tips, see real-world applications, and get advice to debug common issues you might face.
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--OUTPUT--True
False
The equality operator == offers a straightforward way to compare dictionaries. It checks for value equality, returning True only if two dictionaries contain the exact same key-value pairs. The comparison is order-agnostic, which is why dict1 == dict2 evaluates to True even though their keys are in a different sequence.
For the comparison to succeed, two conditions must be met:
- Both dictionaries must have the same set of keys.
- The value for each corresponding key must be identical.
This is why dict1 == dict3 returns False—they have a different value for the key 'b'.
Basic comparison techniques
Beyond the straightforward equality check of the == operator, you'll find other methods for more nuanced comparisons, like checking for identity or subset relationships.
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)--OUTPUT--False
True
The is operator checks for object identity, not value equality. It verifies if two variables point to the exact same object in memory, which is a stricter comparison than ==.
- Even though
dict1anddict2have identical content, they are two separate objects created independently. That’s whydict1 is dict2returnsFalse. - The assignment
dict3 = dict1doesn't create a new dictionary. It makesdict3a reference to the same object asdict1, sodict1 is dict3isTrue.
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}")--OUTPUT--Common items: {('a', 1)}
Items in dict1 but not in dict2: {('b', 2)}
To find specific differences between dictionaries, you can combine the items() method with set operations. The items() method gives you a view of each dictionary's key-value pairs. By converting these views into sets, you can perform powerful comparisons.
- The intersection operator (
&) reveals the key-value pairs that both dictionaries share. - The difference operator (
-) isolates pairs that exist in one dictionary but not the other.
This approach is especially useful for tasks like data validation, where you need to pinpoint exactly what's different.
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()}")--OUTPUT--dict2 is a subset of dict1: True
Keys in dict1 not in dict2: {'c'}
You can determine if one dictionary is a subset of another by checking if all its key-value pairs exist in the larger dictionary. The all() function combined with a generator expression provides an efficient way to do this. It iterates through dict2.items() and confirms each pair is present in dict1.items().
- This method is useful for verifying if a smaller configuration is contained within a larger one.
- To find keys that are unique to the larger dictionary, you can use set operations on the dictionary keys, like
dict1.keys() - dict2.keys().
Advanced comparison methods
When simple equality checks aren't enough, you can turn to more advanced methods for handling nested structures, specific keys, or comparisons requiring a tolerance.
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--OUTPUT--False
True
When your dictionaries contain other dictionaries, the standard == operator handles the comparison recursively. It checks for value equality at every level, ignoring the order of keys throughout the nested structure. That's why dict1 == dict3 correctly evaluates to True.
You might be tempted to serialize dictionaries to strings for comparison, but this approach has a major pitfall.
- Converting dictionaries to JSON strings with
json.dumps()makes the comparison order-sensitive. - Since the inner dictionary keys in
dict1anddict2are in a different order, their string representations don't match, causing the comparison to fail.
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}")--OUTPUT--Dictionaries match on specified keys: False
You don't always need to compare entire dictionaries. To check just a subset of keys, you can specify which ones to include. This is useful when you only care about certain fields and want to ignore others.
A generator expression inside the all() function provides a concise solution:
- You define a list of
keys_to_compare. - The code then iterates through this list, checking if the value for each key is the same in both dictionaries using
dict1.get(k) == dict2.get(k). - The
all()function ensures every comparison isTrue. The example returnsFalsebecause the values for the key'age'are different.
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}")--OUTPUT--Values match within tolerance: True
Sometimes you'll need to check if numerical values are "close enough" rather than identical. This is especially useful when you're dealing with floating-point numbers or measurements. You can build a custom comparison using a generator expression inside the all() function.
- The expression
abs(dict1[k] - dict2[k]) <= 2calculates the absolute difference for each key's value and checks if it's within a tolerance of 2. - The
all()function then confirms that every common key's values meet this condition, returningTrueif they all do.
Move faster with Replit
Replit is an AI-powered development platform that transforms natural language into working applications. Describe what you want to build, and Replit Agent creates it—complete with databases, APIs, and deployment.
For the dictionary comparison techniques we've explored, Replit Agent can turn them into production-ready tools.
- Build a configuration validator that compares two versions of a settings file and highlights the exact key-value pairs that have changed, using set operations on
items(). - Create a data reconciliation tool that checks for discrepancies between two datasets, like customer records from different sources, by identifying non-matching entries.
- Deploy a feature flag dashboard that verifies if a user's settings dictionary is a valid subset of a master configuration, ensuring system integrity.
Describe your app idea, and Replit Agent writes the code, tests it, and fixes issues automatically, all in your browser.
Common errors and challenges
When comparing dictionaries, you might run into issues like missing keys, incompatible data types, or confusion between shallow and deep copies.
Avoiding KeyError when comparing dictionaries with missing keys
A KeyError is a common stumbling block that occurs if you try to access a key that exists in one dictionary but not the other. Instead of direct key access like dict1['key'], you can use the get() method. For example, dict1.get('key') will return None instead of crashing if the key is missing, allowing your comparison logic to proceed smoothly.
Handling TypeError when comparing complex dictionary values
You may also encounter a TypeError when dictionary values are complex objects that Python doesn't know how to compare, such as custom class instances. The standard equality check fails because it lacks instructions for comparing these objects. To fix this, you can define an __eq__ method within your custom class to specify exactly what makes two instances equal.
Shallow vs. deep copy confusion in dictionary comparisons
Finally, confusion between shallow and deep copies can lead to unexpected behavior. A shallow copy of a dictionary with nested objects only copies references to those objects. Modifying a nested object in the copy will also alter the original, skewing your comparison results. To prevent this, use copy.deepcopy() to create a fully independent duplicate of the dictionary and all its contents, ensuring your comparisons are reliable.
Avoiding KeyError when comparing dictionaries with missing keys
A KeyError is a frequent issue when you directly access a key that exists in one dictionary but not the other. Your program will crash because it can't find what you've asked for. The code below demonstrates this with the 'email' key.
def compare_values(dict1, dict2, key):
return dict1[key] == dict2[key]
user1 = {'name': 'Alice', 'email': '[email protected]'}
user2 = {'name': 'Bob', 'phone': '555-1234'}
print(compare_values(user1, user2, 'email')) # KeyError: 'email'
The error occurs because the compare_values function directly accesses user2['email'], but the user2 dictionary doesn't have an 'email' key. The following code shows how to perform this check without causing a crash.
def compare_values(dict1, dict2, key):
return key in dict1 and key in dict2 and dict1[key] == dict2[key]
user1 = {'name': 'Alice', 'email': '[email protected]'}
user2 = {'name': 'Bob', 'phone': '555-1234'}
print(compare_values(user1, user2, 'email')) # False
The solution prevents a KeyError by first confirming the key exists in both dictionaries using the in operator. The expression key in dict1 and key in dict2 performs this safe check before any value access occurs.
- Because Python uses short-circuit evaluation, it won't attempt to compare values if the key is missing from either dictionary, avoiding the error entirely.
This is crucial when comparing dictionaries that may not share the same structure, like user data from different sources.
Handling TypeError when comparing complex dictionary values
A TypeError occurs when you try to perform an operation on incompatible data types. For example, you can't find the intersection of a list and a set using the & operator because Python doesn't support it. The following code demonstrates this exact problem.
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 code fails because it attempts to use the intersection operator (&) on two different data types. The 'tags' value is a list in one dictionary and a set in the other. The corrected code below shows how to handle this.
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'}
The solution is to make sure both values are the same type before you try to compare them. By converting the list from user1['tags'] into a set using set(), the intersection operator (&) can work correctly because it's now comparing two sets.
- This kind of
TypeErroris common when you're working with data from different sources, as the same field might be stored as a list in one place and a set in another.
Shallow vs. deep copy confusion in dictionary comparisons
The difference between shallow and deep copies often causes confusing bugs with nested dictionaries. A shallow copy using copy() doesn't duplicate nested objects, only their references. Modifying the copy can unintentionally alter the original, skewing your comparison results. The following code demonstrates this unexpected behavior.
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 copy() method only duplicates the top-level dictionary, not the nested one. Both original and copy end up pointing to the same user data, so modifying the copy also changes the original, corrupting your source data. The following code shows how to create a truly independent copy to avoid this issue.
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 solution is to use copy.deepcopy(), which creates a completely independent duplicate of the original dictionary and all its nested contents. When you modify the score in the deep_copy, the original dictionary remains untouched. This ensures your comparisons are accurate and you don't accidentally corrupt your source data.
- Always use
copy.deepcopy()when you need to modify a copy of a dictionary that contains other mutable objects, like lists or other dictionaries, without affecting the original.
Real-world applications
Now that you're equipped to handle common errors, you can apply these comparison techniques to real-world tasks like tracking data changes.
Tracking user profile changes with dictionary comparison
A practical application is tracking user profile updates, where you can use a dictionary comprehension with the get() method to safely compare old and new values and log any changes.
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 creates a log of what changed between two user profiles. It starts by combining all keys from both old_profile and new_profile into a single set with the | operator, ensuring every field is checked.
A dictionary comprehension then builds the changes dictionary. For each key, it uses get() to safely compare values. If they differ, it adds the key to changes along with a tuple holding the old and new values. This gives you a neat summary of all updates without causing errors for missing keys.
Visualizing dictionary differences with the difflib module
You can also generate a human-readable, line-by-line report of what's changed between two dictionaries using Python's built-in difflib module, which is especially useful for comparing configuration files.
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)))
This code uses Python's difflib module to pinpoint discrepancies between two dictionaries. To make the comparison work, the data must be prepared first.
- Each dictionary's items are converted into a sorted list of strings. Sorting is crucial because
difflibrequires sequences with a stable order. - The
difflib.ndiff()function then processes these lists, generating a delta that highlights every addition, deletion, and modification. The resulting output clearly marks the lines that differ between the two original configurations.
Get started with Replit
Turn these comparison techniques into a real tool. Tell Replit Agent: “Build a config file diff tool” or “Create a data validator that compares two CSVs and flags mismatched rows.”
The agent writes the code, tests for errors, and deploys your app. Start building with Replit.
Create and deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.
Create & deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.



%2520in%2520Python.png)