-1

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.

joseville
  • 685
  • 3
  • 16
  • 1
    You'd want it to be `defaultdict(lambda: tree())` to produce the same result as `defaultdict(tree)`. Otherwise the `tree` function itself never gets called. – Samwise Oct 08 '21 at 22:15
  • 1
    In the second one, `tree` is one specific defaultdict, and the default values it uses are that same defaultdict. In the first one, each time `tree` is executed it will create a new defaultdict. – khelwood Oct 08 '21 at 22:33
  • @Samwise, thanks. I tried `tree = defaultdict(lambda: tree())`, and in the next line `tree['a']` caused an error: `TypeError: 'collections.defaultdict' object is not callable` ` – joseville Oct 09 '21 at 00:59
  • 1
    You still need the outer `lambda` to make `tree` a function rather than a single `defaultdict` object. So `tree = lambda: defaultdict(tree)` works, as does `tree = lambda: defaultdict(lambda: tree())`. `tree` and each argument to `defaultdict` need to be *callable*. – Samwise Oct 09 '21 at 14:13
  • 1
    @Samwise gotcha, thanks! And since `lambda: tree()` is equivalent to `tree`, that means the longer form `tree = lambda: defaultdict(lambda: tree())` is equivalent to `tree = lambda defaultdict(tree)`. – joseville Oct 10 '21 at 00:09

1 Answers1

1

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.

khelwood
  • 55,782
  • 14
  • 81
  • 108
  • Thanks! I get it now! So with the second one. when I first do `tree['a']`, initially, `tree` does not have an 'a' key so it does 'tree['a'] = (lambda: tree)() = tree`. So after doing `tree['a']`, `tree` looks like `{'a': tree}`. If I then do `tree['b']['c']`, it will basically do `(tree['b'])['c'] = (tree)['c] = tree`, so now 'tree` looks like `{'a': tree, 'b': tree, 'c': tree}`. – joseville Oct 09 '21 at 00:46