I have the following code:
from collections import *
nested_dict = lambda: defaultdict(nested_dict)
data = nested_dict()
which enables me to write any new "path" in the dict as a one liner:
data['A']['B']['C']=3
which is what I want. But I want to get an exception when running (for any non existing path):
var = data['A']['XXX']['C']
I feel I need defaultdict when writing, plain dict when reading...
Or, is there a simple nice way to check if a 'path' exists in a defaultdict without modifying its contents...
I tried converting the defaultdict back to a dict before the lookup, hoping that:
dict(data)['A']['XXX']['C']
would raise a exception... but it kept creating missing keys...
An obvious solution is to just use plain dicts with a function that can "materialize" the intermediate keys:
def write_path(d, path, value):
for key in path[:-1]:
d = d.setdefault(key, {})
d[path[-1]] = value
d = {}
write_path(d, ['a', 'b', 'c'], 3)
print(d)
print(d['a']['b']['c'])
print(d['a']['b']['d'])
outputs
{'a': {'b': {'c': 3}}}
3
Traceback (most recent call last):
File "writedefaultdict.py", line 11, in <module>
print(d['a']['b']['d'])
KeyError: 'd'
You can't distingsuish between lookups and writes here, because it is the lookups that create your intermediary structure in the data['A']['B']['C'] = 3
assignment. Python executes the indexing operations data['A']
and then ['B']
first, before assigning to the 'C'
key. The __getitem__
, __setitem__
and __missing__
hooks involved to make that work are not given enough context to distinguish between access that then leads to the 'C'
assignment from only 'reading' 'XXX'
in your second example.
You really only have 3 options here:
Don't use defaultdict
. When writing, explicitly create new nested dictionaries with dict.setdefault()
instead; you can chain these calls as needed:
var = {} var.setdefault('A', {}).setdefault('B', {})['C'] = 3
or you can wrap recursive behaviour in a few functions .
Create a recursive copy of your defaultdict
structure to replace it with a dict
structure once you are done writing:
def dd_to_d(dd): r = {} stack = [(r, dd)] while stack: target, dd = stack.pop() for key, value in dd.items(): if isinstance(value, defaultdict): sub = {} stack.append((sub, value)) value = sub target[key] = value return r var = dd_to_d(var)
Set all the default_factory
attributes to None
to disable creating new values for missing keys:
def disable_dd(dd): stack = [dd] while stack: dd = stack.pop() dd.default_factory = None for key, value in dd.items(): if isinstance(value, defaultdict): stack.append(value) disable_dd(var)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.