Sometimes it's nice to be able to use a dictionary like an object (so that you don't have to always write mydict['blah']
. Instead, you can write mydict.blah
In Python 3, there are new rules about overriding/wrapping a python dictionary. In Python2, it was enough to wrap the __getattr__
and __setattr__
methods. When you wrap these things, you get some nice capabilities to add some special handling when a certain attribute was added (e.g. you could clean/filter data in/out of a dict).
A situation where this is helpful is in a Flask template (an HTML template). Using a __getattr__
filter, you can format data as it leaves the dict
. This way, in the template (where Python expressions can look a little complex) you can keep things simple in there by just writing mymodel.blah
and knowing that the text coming out of blah
is already the way you want it.
In Python3 wrapping a dict
is a little messy. I"m not sure how to make it work now. Here are two rough implementations that are not working well:
# messed up Python3 wrapped dictionary (sets work, but gets do not)
class AttrDict(dict):
def __init__(self, *args, **kwargs):
super(AttrDict, self).__init__(*args, **kwargs)
self.__dict__ = self
self.clean_strings()
def clean_strings(self):
for key, value in self.items():
self[key] = string_empty_to_none(value)
def __getattr__(self, name):
#this never gets called in Python 3
return self[name]
Here's another one:
# sets don't work - Always throws: TypeError: 'AttrDict' object does not support item assignment
class AttrDict():
def __init__(self, *args, **kwargs):
self.data = dict()
self.data.__init__(*args, **kwargs)
self.clean_strings()
def clean_strings(self):
for key, value in self.data.items():
self.data[key] = string_empty_to_none(value)
def __getattr__(self, attr):
if attr in self.data:
return self.data[attr]
else:
raise AttributeError("--%r object has no attribute %r" % (type(self).__name__, attr))
def __setattr__(self, name, value):
if name == 'data':
super(AttrDict, self).__setattr__(name, value)
else:
self.data[name] = value
Here's what my little utility method looks like:
def string_empty_to_none(s):
if type(s) == str:
return None if (s.strip() == '') else s
return s
I know that in Python3, you are supposed to use __getattribute__
instead of __getattr__
But that always seems to end up in an infinite loop when I do this.
NOTE: that the final syntax that I'm looking for is like this:
>>> x = AttrDict({'some': 'value'})
>>> x['blah'] = 'hello world'
>>> print(x.blah)
hello world
>>> print(x.some)
value