Last active
August 30, 2018 14:11
-
-
Save vstoykov/3c3367e746ee3ccdc26e030f869cfbd5 to your computer and use it in GitHub Desktop.
Simple dict sucblass for making it possible to access keys as attributes
This file contains hidden or 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
from collections import defaultdict | |
class DictObject(dict): | |
""" | |
Simple dict subclass for making it possible to to access keys as attributes | |
This class can be used as object_hook when deserializing JSON for easy | |
access to attributes of the JSON objects. | |
Example:: | |
>>> import json | |
>>> result = json.loads('{"a": 1, "b": 2}', object_hook=DictObject) | |
>>> result | |
DictObject({'a': 1, 'b': 2}) | |
>>> result.a == result['a'] == 1 | |
True | |
>>> result.b == result['b'] == 2 | |
True | |
>>> result.c = 3 | |
>>> result.c == result['c'] == 3 | |
True | |
>>> result | |
DictObject({'a': 1, 'b': 2, 'c': 3}) | |
>>> del result.a | |
>>> result | |
DictObject({'b': 2, 'c': 3}) | |
>>> result.a | |
Traceback (most recent call last): | |
... | |
AttributeError: 'DictObject' object has no attribute 'a' | |
>>> del result.a | |
Traceback (most recent call last): | |
... | |
AttributeError: 'DictObject' object has no attribute 'a' | |
""" | |
# We are defining empty __slots__ because we will use the keys in | |
# self as attributes and we do not want the resulting object to | |
# have __dict__ which will waste memory. | |
__slots__ = () | |
def __getattr__(self, attr): | |
try: | |
return self[attr] | |
except KeyError: | |
raise AttributeError("'{}' object has no attribute {!r}".format(self.__class__.__name__, attr)) | |
def __setattr__(self, attr, val): | |
self[attr] = val | |
def __delattr__(self, attr): | |
try: | |
del self[attr] | |
except KeyError: | |
raise AttributeError("'{}' object has no attribute {!r}".format(self.__class__.__name__, attr)) | |
def __dir__(self): | |
return list(sorted(set(super().__dir__()).union(self))) | |
def __repr__(self): | |
return '{}({})'.format(self.__class__.__name__, dict.__repr__(self)) | |
class DefaultDictObject(DictObject, defaultdict): | |
""" | |
Implements DictObject as defaultdict | |
Example:: | |
>>> d = DefaultDictObject(list) | |
>>> d | |
DefaultDictObject(<class 'list'>, {}) | |
>>> [] == d.something == d['something'] | |
True | |
>>> d = DefaultDictObject(list, {'a': [1, 2, 3]}) | |
>>> d | |
DefaultDictObject(<class 'list'>, {'a': [1, 2, 3]}) | |
>>> [1, 2, 3] == d.a == d['a'] | |
True | |
""" | |
# We are redefining __slots__ to be empty because of the same reason | |
# as in DictObject - to not waste memory. | |
__slots__ = () | |
def __repr__(self): | |
return '{}({!r}, {})'.format(self.__class__.__name__, | |
self.default_factory, | |
dict.__repr__(self)) | |
if __name__ == "__main__": | |
import doctest | |
doctest.testmod() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment