To view the two currently existing answers, first import timeit :
import timeit
Now we need to install the code:
setup = ''' import copy def getshape(d): if isinstance(d, dict): return {k:getshape(d[k]) for k in d} else: # Replace all non-dict values with None. return None def nneo_shape_equal(d1, d2): return getshape(d1) == getshape(d2) def aaron_shape_equal(d1,d2): if isinstance(d1, dict) and isinstance(d2, dict): return (d1.keys() == d2.keys() and all(aaron_shape_equal(d1[k], d2[k]) for k in d1.keys())) else: return not (isinstance(d1, dict) or isinstance(d2, dict)) class Vividict(dict): def __missing__(self, key): value = self[key] = type(self)() return value d = Vividict() d['foo']['bar'] d['foo']['baz'] d['fizz']['buzz'] d['primary']['secondary']['tertiary']['quaternary'] d0 = copy.deepcopy(d) d1 = copy.deepcopy(d) d1['primary']['secondary']['tertiary']['extra'] # d == d0 is True # d == d1 is now False! '''
Now let's check two options: first with Python 3.3!
>>> timeit.repeat('nneo_shape_equal(d0, d); nneo_shape_equal(d1,d)', setup=setup) [36.784881490981206, 36.212246977956966, 36.29759863798972]
And it looks like my solution takes from 2/3 to 3/4 times, which makes it more than 1.25 times faster.
>>> timeit.repeat('aaron_shape_equal(d0, d); aaron_shape_equal(d1,d)', setup=setup) [26.838892214931548, 26.61037168605253, 27.170253590098582]
And in Python 3.4 (alpha), which I compiled myself:
>>> timeit.repeat('nneo_shape_equal(d0, d); nneo_shape_equal(d1,d)', setup=setup) [272.5629618819803, 273.49581588001456, 270.13374400604516] >>> timeit.repeat('aaron_shape_equal(d0, d); aaron_shape_equal(d1,d)', setup=setup) [214.87033835891634, 215.69223327597138, 214.85333003790583]
Still, about the same ratio. The time difference between them is likely, because I independently compiled 3.4 without optimization.
Thanks to all the readers!