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
dby settingd.__dict__toditself!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 theallowDottingmethod again!)Now you might be wondering if this deletes all the key-value pairs in
ditself. 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
DottableDictis given a__dict__attribute in__init__, it seems cleaner to me to allow instances ofDottableDictto always have a__dict__attribute.In the comments you note:
To preserve attributes such as
d.foowhich were set whileallowDottinghas 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.