Home > other >  Nested defaultdict raises AttributeError
Nested defaultdict raises AttributeError

Time:12-07

I am using a nested defaultdict to keep code clean and reduce the redundant code.

I am constructing a dictionary such as:

{"Store1": {"Product1": 1}, "Store2": {"Product1": 2, "Product2": 1}}

I tried to implement the answer to this question Nested dictionary with defaults, which would raise the exception:

AttributeError: 'int' object has no attribute 'items'
from collections import defaultdict, Counter

d = defaultdict(lambda: defaultdict(lambda: Counter()))
d["Store1"]["Product1"]  = 1
print(d)

is there anyway I can for example:

d["Store1"]["Product1"]  = 1

CodePudding user response:

When you use the following

d = defaultdict(lambda: defaultdict(lambda: Counter()))
d["Store1"]["Product1"]  = 1

then, d["Store1"] will create a new element of "type" defaultdict(lambda: Counter()) and thus d["Store1"]["Product1"] will create a new element of type Counter. Hence when you do = 1, it attempts to augment the Counter object by 1. This, however, is not defined, as the types are not compatible:

>>> c = Counter()
>>> c  = 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/---/lib/python3.7/collections/__init__.py", line 832, in __iadd__
    for elem, count in other.items():
AttributeError: 'int' object has no attribute 'items'

As you can observe, it assumes that the right hand side follows the Mapping protocol and it attempts to add the r.h.s. counts to its own. I.e. something like:

>>> c = Counter(a=1)
>>> c  = Counter(a=2, b=3)
>>> c
Counter({'a': 3, 'b': 3})

When you use defaultdict(lambda: defaultdict(int)) on the other hand, then d["Store1"]["Product1"] creates a new int object, which can be incremented by = 1 and written back to the dict.

CodePudding user response:

You need to write something like that

from collections import defaultdict, Counter

d = defaultdict(lambda: defaultdict(lambda: Counter()))
d["A"]["B"]["Store1"]  = 1
print(d)

CodePudding user response:

I think you have one too many defaultdicts defined. The following works fine:

d = defaultdict(lambda: Counter())
d["Store1"]["Product1"]  = 1
print(d)

Output:

{'Store1': {'Product1': 1}}
  • Related