Skip to content

Instantly share code, notes, and snippets.

@M0r13n
Created October 11, 2020 14:32
Show Gist options
  • Save M0r13n/64a3f68a5df38f6523eb7977656a7928 to your computer and use it in GitHub Desktop.
Save M0r13n/64a3f68a5df38f6523eb7977656a7928 to your computer and use it in GitHub Desktop.
A simple Python dictionary whose keys can be queried using dot notation.
from typing import Any
class CaseInsensitiveDict(dict):
"""
A dict object where the case of keys does not matter.
Create a new instance:
>>> c = CaseInsensitiveDict({'a' : 123})
then you access the values normally
>>> assert c['a'] == c['A']
"""
def __getitem__(self, k: str) -> Any:
"""
Invoked when a key is accessed.
"""
if k in self:
return super().__getitem__(k)
return self._get_item_case_insensitive(k)
def _get_item_case_insensitive(self, key: str):
"""
Iterate over every key and compare it's lowercase representation
with a given key.
"""
key = key.lower()
for k, v in self.items():
lk = k.lower()
if lk == key:
return v
raise KeyError(f'{key}')
class DotDict(CaseInsensitiveDict):
"""
A single level dict object that supports accessing via dot notation.
>>> x = DotDict()
>>> x.a = "A value"
>>> assert x['a'] == x.a
>>> del x.a
>>> assert 'a' not in x
"""
def __getattr__(self, attr: str) -> Any:
"""
Pass attribute call to getitem
"""
return self.__getitem__(attr)
def __setattr__(self, key: str, val: Any) -> None:
"""
Pass attribute assignment to setitem
"""
self.__setitem__(key, val)
def __delattr__(self, item: str) -> None:
"""
Pass attribute deletion to delitem
"""
self.__delitem__(item)
class NestedDotDict(DotDict):
"""
Multi-level DotDict where each level is itself a DotDict.
Builds the final object recursively.
Makes it possible to use chained dot operations:
>>> sample = {
... "A": {
... "B": {
... "C": "Hello"
... },
... "D": "Nice"
... },
... "test": 42
... }
>>> n = NestedDotDict(sample)
>>> print(n["A"])
{'B': {'C': 'Hello'}, 'D': 'Nice'}
>>> print(n.a.b.c)
Hello
>>> print(n.TEST)
42
"""
def __init__(self, *args: Any, **kwargs) -> Any:
super().__init__(*args, **kwargs)
self.to_dot_dict()
def to_dot_dict(self):
"""
Iterate over every key and transform every value that is a normal dict
into a NestedDotDict recursively.
"""
for k, v in self.items():
if isinstance(v, dict):
if not isinstance(v, DotDict):
self[k] = NestedDotDict(v)
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