Using the `==` operator on circularly defined dictionaries


Python allows dictionaries to be compared with ==

import copy

child = {'name': 'child'}
parent_1 = {'name': 'parent', 'child': child}
parent_2 = copy.deepcopy(parent_1)
print(parent_1 == parent_2)

Prints True, as you would expect it to.

Python also allows dictionaries to reference each other circularly.

child = {'name': 'child'}
parent_1 = {'name': 'parent', 'child': child}
child['parent'] = parent_1  # Create the circular reference

However, trying to use the == operator on dictionaries with circular references raises an error.

parent_2 = copy.deepcopy(parent_1)
print(parent_1 == parent_2)


C:\Python34\python.exe -i C:/Users/anon/.PyCharm40/config/scratches/scratch_5
Traceback (most recent call last):
  File "C:/Users/anon/.PyCharm40/config/scratches/scratch_5", line 11, in <module>
    print(parent_1 == parent_2)
RuntimeError: maximum recursion depth exceeded in comparison

How can I check two dictionaries with circular references for equality?

You need to define what you mean by equal. Normally "equal" for dictionaries means 'all the key/value pairs are the "equal"'. If a dictionary has a reference to itself, this definition of equal may lead to a recursive definition, i.e. a == b iff a == b.

Take this simple example:

a = {}; a['item'] = a
b = {}; b['item'] = b

Are a and b equal? In order to know that, you need to first know if a and b are equal ...

You could create a special equal that looks something like this:

def equal(a, b, special=[]):
    if not isinstance(a, dict) or not isinstance(b, dict):
        return a == b

    special = special + [a, b]
    set_keys = set(a.keys())
    if set_keys != set(b.keys()):
        return False

    for key in set_keys:
        if any(a[key] is i for i in special):
        elif any(b[key] is i for i in special):
        elif not equal(a[key], b[key], special):
            return False
    return True