Created
June 20, 2018 20:07
-
-
Save bsolomon1124/d5b8734f9722935093d9d470780faed6 to your computer and use it in GitHub Desktop.
Dictionary subclasses supporting dot access for keys.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""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