I haven't seen a toggleable version of dictionary dot-access yet.
My first-pass attempt here doesn't work:
class DottableDict (dict):
def allowDotting (self, state=True):
if state:
self.__setattr__ = dict.__setitem__
self.__getattr__ = dict.__getitem__
else:
del self.__setattr__
del self.__getattr__
>>> import dot
>>> d = dot.DottableDict()
>>> d.allowDotting()
>>> d.foo = 'bar'
>>> d
{}
>>> d.foo
'bar'
>>> d.__dict__
{'__setattr__': <slot wrapper '__setitem__' of 'dict' objects>, 'foo': 'bar',
'__getattr__': <method '__getitem__' of 'dict' objects>}
>>> d.allowDotting(False)
>>> d.__dict__
{'foo': 'bar'}
I think the signatures don't match up between setattr and setitem.
My second pass also seems like it should work, but fails in the same ways:
class DottableDict (dict):
def dotGet (self, attr):
return dict.__getitem__(self, attr)
def dotSet (self, attr, value):
return dict.__setitem__(self, attr, value)
def allowDotting (self, state=True):
if state:
self.__getattr__ = self.dotGet
self.__setattr__ = self.dotSet
else:
del self.__setattr__
del self.__getattr__
If you set
self.__dict__ = self
, then the dict will automatically become "dottable". You can turn off the "dot-ability" by settingself.__dict__ = {}
. The key-value pairs will still be accessible through indexing, however. This idea comes mainly from katrielalex's bio page:Remember the Zen of Python, however:
By restricting yourself to the standard syntax for dict access, you improve readability/maintainability for yourself and others.
How it works:
When you type
d.foo
, Python looks for'foo'
in a number of places, one of which is ind.__dict__
. (It also looks ind.__class__.__dict__
, and all the__dict__
s of all the bases listed ind.__class__.mro()
... For the full details of attribute lookup, see this excellent article by Shalabh Chaturvedi).Anyway, the important point for us is that all the key-value pairs in
d.__dict__
can be accessed with dot notation.That fact means we can get dot-access to the key-value pairs in
d
by settingd.__dict__
tod
itself!d
, after all, is a dict, andd.__dict__
expects a dict-like object. Notice this is also memory-efficient. We are not copying any key-value pairs, we're simplying directingd.__dict__
to an already existent dict.Furthermore, by assigning
d.__dict__
todict()
, we effectively turn off dot-access to the key-value pairs ind
. (This does not completely disable dot-access -- key-value pairs ind.__class_.__dict__
for instance, can still be accessed through dot notation. Thank goodness that's true or you wouldn't be able to call theallowDotting
method again!)Now you might be wondering if this deletes all the key-value pairs in
d
itself. The answer is no.The key-value pairs are not stored in the
__dict__
attribute. In fact, a normal dict does not have a__dict__
attribute. So settingd.__dict__ = {}
simply resets the dict to a neutral condition. We could have usedinsteaad of
too. However, since the
DottableDict
is given a__dict__
attribute in__init__
, it seems cleaner to me to allow instances ofDottableDict
to always have a__dict__
attribute.In the comments you note:
To preserve attributes such as
d.foo
which were set whileallowDotting
has been turned off, you'll need to store the alternate dict to whichself.__dict__
has been set.By conventional, attributes that start with a single underscore are understood to be private, implementation details. I'm extending the convention here by introducing a private key,
'_attribute'
into the dict.