Skip to content

Instantly share code, notes, and snippets.

@bsolomon1124
Created June 20, 2018 20:07
Show Gist options
  • Save bsolomon1124/d5b8734f9722935093d9d470780faed6 to your computer and use it in GitHub Desktop.
Save bsolomon1124/d5b8734f9722935093d9d470780faed6 to your computer and use it in GitHub Desktop.
Dictionary subclasses supporting dot access for keys.
"""Dictionary subclasses supporting dot access for keys."""
__all__ = ['AccessDict', 'SafeAccessDict']
__author__ = 'Brad Solomon <bsolomon@protonmail.com>'
class AccessDict(dict):
"""Dictionary with dot access, setting, and deletion of values.
Otherwise, behaves the same. (KeyError is still possible.)
>>> b = AccessDict(a=1, b=2, c={'f': False, 't': True})
>>> b.a
1
>>> type(b.c)
<class 'AccessDict'>
>>> b.d = 5
>>> b
{'a': 1, 'b': 2, 'c': {'f': False, 't': True}, 'd': 5}
"""
# TODO: __str__/__repr__
_check = dir({})
def __init__(self, *args, **kwargs):
for k in kwargs:
if k in self._check:
raise ValueError('You are attempting to use a key with'
' a name that is already\n an attribute'
' of a dictionary instance: `%s`' % k)
self.update(*args, **kwargs)
# `__getitem__()` and `__setitem__()` don't need to be touched.
# For differences between `__getattr__()` and `__getattribute__()`:
# https://docs.python.org/3/reference/datamodel.html
def __getattr__(self, key):
val = dict.__getitem__(self, key)
# Do not use `isintance` -- it will catch inheritance
# when we don't want it to.
return AccessDict(val) if type(val) is dict else val
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
# This is done by scikit-learn's `Bunch` class; not sure what
# the intent is...
#
# def __dir__(self):
# return self.keys()
def __setstate__(self, state):
# https://github.com/scikit-learn/scikit-learn/issues/6196
pass
class SafeAccessDict(AccessDict):
"""Dictionary with dot access, setting, and deletion of values.
Another key feature is item access defaults to None rather
than raising KeyError.
>>> s = SafeAccessDict(a=1, b=2, c={'f': False, 't': True})
>>> s.a
1
>>> type(s.c)
<class 'SafeAccessDict'>
>>> s.d = 5
>>> s
{'a': 1, 'b': 2, 'c': {'f': False, 't': True}, 'd': 5}
>>> s.c.f
False
Be careful with nested access. The get() -> default feature is
*only* valid if the nested item is a dictionary/subclass thereof.
>>> sb.c.f.a
AttributeError: 'bool' object has no attribute 'a'
"""
def __getattr__(self, key):
val = dict.get(self, key)
return SafeAccessDict(val) if type(val) is dict else val
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment