Search code examples
pythontreenestedtrierecursive-datastructures

Understanding nested defaultdict and `tree = lambda: defaultdict(tree)` vs `tree = defaultdict(lambda: tree)`


Working form

From another question, I see how to create a defaultdict of defaultdict of defaultdict... as:

Working form Using it Output
tree = lambda: defaultdict(tree)
x = tree()
x["1"]
x["2"]
x["1"]["3"]

print(json.dumps(x))
{"1": {"2": {}}, "2": {}}

It works as desired, but I'm having trouble understanding it.


Non-working form

Also, why does the following not work at all:

Non Working form Using it Output
tree = defaultdict(lambda: tree)
x = tree
x["1"]
x["2"]
x["1"]["3"]

print(json.dumps(x))
ValueError: Circular reference detected

Can someone explain, how tree = lambda: defaultdict(tree) works and why tree = defaultdict(lambda: tree) doesn't work?


EDIT: @Samwise notes in the comments that tree and each argument to defaultdict need to be callable, so to make the second form work, it'd need to be:

tree = lambda: defaultdict(lambda: tree())

but since lambda: tree() is equivalent to tree, this revised form is equivalent to the first form.


Solution

  • Here:

    tree = lambda: defaultdict(tree)
    

    tree is a function, and each time it is executed, it creates a defaultdict. The default values of the defaultdict are given by calling tree again, which creates a new defaultdict each time.


    Here:

    tree = defaultdict(lambda: tree)
    

    tree is one specific defaultdict. The default values of the defaultdict are given by a function that returns tree, the same specific defaultdict. So the default values of the dictionary are itself.