Skip to content

Instantly share code, notes, and snippets.

@majgis
Created September 5, 2012 09:03
Show Gist options
  • Save majgis/3633724 to your computer and use it in GitHub Desktop.
Save majgis/3633724 to your computer and use it in GitHub Desktop.
DictWrap: Create or access massive nested dictionaries with ease.
class DictWrap(object):
""" Wrap an existing dict, or create a new one, and access with either dot
notation or key lookup.
The attribute _data is reserved and stores the underlying dictionary.
When using the += operator with create=True, the empty nested dict is
replaced with the operand, effectively creating a default dictionary
of mixed types.
args:
d({}): Existing dict to wrap, an empty dict is created by default
create(True): Create an empty, nested dict instead of raising a KeyError
example:
>>>dw = DictWrap({'pp':3})
>>>dw.a.b += 2
>>>dw.a.b += 2
>>>dw.a['c'] += 'Hello'
>>>dw.a['c'] += ' World'
>>>dw.a.d
>>>print dw._data
{'a': {'c': 'Hello World', 'b': 4, 'd': {}}, 'pp': 3}
"""
def __init__(self, d=None, create=True):
if d is None:
d = {}
supr = super(DictWrap, self)
supr.__setattr__('_data', d)
supr.__setattr__('__create', create)
def __getattr__(self, name):
try:
value = self._data[name]
except KeyError:
if not super(DictWrap, self).__getattribute__('__create'):
raise
value = {}
self._data[name] = value
if hasattr(value, 'items'):
create = super(DictWrap, self).__getattribute__('__create')
return DictWrap(value, create)
return value
def __setattr__(self, name, value):
self._data[name] = value
def __getitem__(self, key):
try:
value = self._data[key]
except KeyError:
if not super(DictWrap, self).__getattribute__('__create'):
raise
value = {}
self._data[key] = value
if hasattr(value, 'items'):
create = super(DictWrap, self).__getattribute__('__create')
return DictWrap(value, create)
return value
def __setitem__(self, key, value):
self._data[key] = value
def __iadd__(self, other):
if self._data:
raise TypeError("A Nested dict will only be replaced if it's empty")
else:
return other
def __call__(self, keys, default=0):
"""Normalize dictionary
Recursively adds keys only to dicts that contain non-dict
values, ie. if all values are dicts, then keys are not added
args:
keys: iterable of dictionary keys to create
default: default value for each key (deep copy)
"""
root_normalized = False
for child in self._data.values():
if isinstance(child, Mapping):
DictWrap(child)(keys, default)
elif not root_normalized:
for key in keys:
if self._data.get(key, None) is None:
self._data[key] = deepcopy(default)
root_normalized = True
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment