1

I am using the snippet from this answer, more specifically the one that uses a lambda.

I have been trying to implement this using the := operator in Python 3.8 and I ended up with two different implementations, both of which I believe does what I want.

Snippet 1 (using a lambda defined beforehand, same as snippet in linked answer)

from collections import defaultdict
from pprint import pprint

func = lambda:defaultdict(func)

infinite_nested_dicts_one = func()

infinite_nested_dicts_one[1][2][3][4][5]['a'] = '12345a'
infinite_nested_dicts_one[1][2][3][4][5]['b'] = '12345b'

print(infinite_nested_dicts_one[1][2][3][4][5]['a'])  # '12345a'
print(infinite_nested_dicts_one[1][2][3][4][5]['b'])  # '12345b'
pprint(infinite_nested_dicts_one)

Output 1

12345a
12345b
defaultdict(<function <lambda> at 0x000001A7405EA790>,
            {1: defaultdict(<function <lambda> at 0x000001A7405EA790>,
                            {2: defaultdict(<function <lambda> at 0x000001A7405EA790>,
                                            {3: defaultdict(<function <lambda> at 0x000001A7405EA790>,
                                                            {4: defaultdict(<function <lambda> at 0x000001A7405EA790>,
                                                                            {5: defaultdict(<function <lambda> at 0x000001A7405EA790>,
                                                                                            {'a': '12345a',
                                                                                             'b': '12345b'})})})})})})

Snippet 2 (using assignment operator)

from collections import defaultdict
from pprint import pprint

infinite_nested_dicts_two = (func:=defaultdict(lambda:func))

infinite_nested_dicts_two[1][2][3][4][5]['a'] = '12345a'
infinite_nested_dicts_two[1][2][3][4][5]['b'] = '12345b'

print(infinite_nested_dicts_two[1][2][3][4][5]['a'])  # '12345a'
print(infinite_nested_dicts_two[1][2][3][4][5]['b'])  # '12345b'
pprint(infinite_nested_dicts_two)

Output 2

12345a
12345b
defaultdict(<function <lambda> at 0x0000022F1A0EA790>,
            {1: <Recursion on defaultdict with id=2401323545280>,
             2: <Recursion on defaultdict with id=2401323545280>,
             3: <Recursion on defaultdict with id=2401323545280>,
             4: <Recursion on defaultdict with id=2401323545280>,
             5: <Recursion on defaultdict with id=2401323545280>,
             'a': '12345a',
             'b': '12345b'})

In both cases accessing the outer most defaultdict for [1][2][3][4][5]['a'] and [1][2][3][4][5]['b'] gives me the same output.

So the question is

  • Why is the pprint output different?
  • Are these identical or not?
  • Are they both arbitrary nested defaultdict of defaultdict?
  • Am I missing some details?

PS :

I am aware the the syntactical equivalent using := is infinite_nested_dicts_two = (func:=lambda:defaultdict(func))().

Do let me know if any clarification is needed. Many Thanks.

Alice
  • 115
  • 6
  • Hello. I just want to let you know there's no need to say *"Many Thanks."*. It's common SO policy to remove those for several reasons, see [Should 'Hi', 'thanks', taglines, and salutations be removed from posts?](https://meta.stackexchange.com/questions/2950). Also, there is no need to include *"Do let me know if any clarification is needed."* in the body of the post. It's common practice on SO that if any clarification is necessary other users will request it in the comments. – bad_coder Sep 04 '21 at 22:11

1 Answers1

2

Your second version assigns a defaultdict instance to func (and, redundantly, to infinite_nested_dicts_two). Accordingly, instead of using func (which is not a function!) as its default-generating function, it uses a constant function that always returns funci.e., the dictionary itself is the default value. The necessary result is the heavily recursive structure revealed by pprint, since every level of indexing adds a key to, and returns, the same object.

Davis Herring
  • 36,443
  • 4
  • 48
  • 76
  • thank you fellow Whovian ;), I understand it better now, so it could have been `infinite_nested_dicts_one = defaultdict(lambda:infinite_nested_dicts_one)` and I would have got the same output then I believe? So is this somewhat similar to the result of `a = a[0] = [0]` which returns `[[...]]`? I would appreciate if you can clarify this – Alice May 30 '21 at 05:18
  • one other question, will there be increased memory usage due this or as they are all references, it would not matter? sorry for the questions, and thanks again – Alice May 30 '21 at 05:22
  • 1
    @Alice: Yes, that assignment would be equivalent, and the object is circular. In general, additional entries in (the) one dictionary use less memory than multiple dictionaries, but since the semantics are entirely different I’m not sure that matters. – Davis Herring May 30 '21 at 05:30