"""Contains :class:`~.AttrDict`"""
import collections
[docs]class AttrDict(collections.abc.MutableMapping):
"""A wrapper around :class:`dict` that also lets you access keys as attributes.
Parameters
----------
name_or_elems : :class:`str` or :class:`dict`, optional
If a :class:`str`, then it will generate a new type
with that name that inherits from :class:`AttrDict`.
If a :class:`dict`, then it will use it as its
underlying dictionary.
If unspecified, then the initial underlying dictionary
will be empty.
**kwargs
Will be used with :meth:`dict.update` to update
the underlying dictionary.
Examples
--------
>>> import dolor
>>> MyAttrDict = dolor.util.AttrDict("MyAttrDict")
>>> MyAttrDict.__name__
'MyAttrDict'
>>> x = MyAttrDict(y=0)
>>> x.y
0
>>> x["y"]
0
"""
def __new__(cls, name_or_elems=None, **kwargs):
if not isinstance(name_or_elems, str):
return super().__new__(cls)
return type(name_or_elems, (cls,), {})
def __init__(self, elems=None, **kwargs):
if elems is None:
elems = {}
elems.update(kwargs)
self._elems = elems
def __getitem__(self, key):
return self._elems[key]
def __setitem__(self, key, value):
self._elems[key] = value
def __delitem__(self, key):
del self._elems[key]
def __len__(self):
return len(self._elems)
def __iter__(self):
return self._elems.__iter__()
def __getattr__(self, attr):
try:
return self._elems[attr]
except KeyError:
raise AttributeError
def __setattr__(self, attr, value):
if attr == "_elems":
super().__setattr__(attr, value)
return
self._elems[attr] = value
def __delattr__(self, attr):
if attr == "_elems":
super().__delattr__(attr)
return
del self._elems[attr]
def __repr__(self):
return f"{type(self).__name__}({self._elems})"