So I've recently come up with a botched abomination of a class while I was trying to implement some sort of DotDict
that would:
- Enable key-value dot-access.
- Accept a
defaults
Namespace
/Dataclass
as an extra argument containing default values. - Perform a modified
get()
when dot-accessed, by checking if a default value can be found indefaults
, else returnNone
.
Also, the class should recursively convert any dict
found in its values upon initialisation and for each new value into a Dotdict
.
Here's how it looks:
class DotDict(dict):
def __init__(self, base_dict=None, defaults=None):
self.defaults = defaults if defaults is not None else SimpleNamespace()
base_dict = base_dict if base_dict is not None else {}
super().__init__(**base_dict)
for key, value in base_dict.items():
self[key] = self._convert(value)
def __getattr__(self, key):
if key in self:
return self[key]
elif hasattr(self.defaults, key):
return getattr(self.defaults, key)
else:
return None
def __setattr__(self, key, value):
self[key] = value
def __delattr__(self, key):
try:
del self[key]
except KeyError as error:
raise AttributeError(error)
def _convert(self, element):
if isinstance(element, dict):
return DotDict(
{key: self._convert(value) for key, value in element.items()},
defaults=self.defaults,
)
elif isinstance(element, list):
return [self._convert(subelement) for subelement in element]
else:
return element
Although it probably goes against all common sense and about a dozen best practices, the thing sort of works - except for one detail: I have yet to find a way to assign de default
attribute without adding it to the instances' items()
. This makes sense, as the __setattr__
method is immediately overridden by its dot-access version.
Edited for clarity
The above paragraph was so terribly phrased as to, in effect, ask the wrong question. I am aware there are a few ways to assign attributes without resorting to the classe's own __setattr__
, as kindly pointed out by user2357112 supports Monica
, which would indeed solve this specific issue.
The code described above was meant as an illustration of the process that led me to ask myself the actual question of this post. Reading the whole thing with fresh eyes now makes me realise that most of the original question's details were at best unnecessary, and likely confusing. I'll leave it up for transparency, but if I was to rewrite the question from scratch, it would simply be:
Is there any way to overwrite any class method after the instance has been initialised?