This recipe allows you to present dictionary based nested data stuctures in your Python code as objects with nested attributes.
It provides dot ('.') based attribute lookups, like this :-
>>> x = d.foo.bar.baz
instead of the usual (and longer) Python dictionary syntax lookups :-
>>> x = d['foo']['bar']['baz']
This recipe saves you lot of typing!
For simplicity and readability this particular version show a read only lookup class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | #!/usr/bin/env python
import pprint
class DictDotLookup(object):
"""
Creates objects that behave much like a dictionaries, but allow nested
key access using object '.' (dot) lookups.
"""
def __init__(self, d):
for k in d:
if isinstance(d[k], dict):
self.__dict__[k] = DictDotLookup(d[k])
elif isinstance(d[k], (list, tuple)):
l = []
for v in d[k]:
if isinstance(v, dict):
l.append(DictDotLookup(v))
else:
l.append(v)
self.__dict__[k] = l
else:
self.__dict__[k] = d[k]
def __getitem__(self, name):
if name in self.__dict__:
return self.__dict__[name]
def __iter__(self):
return iter(self.__dict__.keys())
def __repr__(self):
return pprint.pformat(self.__dict__)
if __name__ == '__main__':
cfg_data = eval("""{
'foo' : {
'bar' : {
'tdata' : (
{'baz' : 1 },
{'baz' : 2 },
{'baz' : 3 },
),
},
},
'quux' : False,
}""")
cfg = DictDotLookup(cfg_data)
# Standard nested dictionary lookup.
print 'normal lookup :', cfg['foo']['bar']['tdata'][0]['baz']
# Dot-style nested lookup.
print 'dot lookup :', cfg.foo.bar.tdata[0].baz
|
I like storing configuration data for my scripts in files on disk as simple nested dictionaries and lists or tuples using standard Python syntax. While 'flat is better than nested' some data is best represented in a nested way.
Apart from the inherent security issues associated with loading this type of data from untrusted sources, this way of representing configuration data is easy to create, read, change and diff using your favourite text editor/IDE and is also incredibly quick and simple to dump and load using standard Python calls and modules.
One thing that has bugged me for a good while now is having to type a lot of brackets and quotes once the data nesting gets past 2 to 3 levels deep.
I came up with this fairly simple solution so I didn't have to do so as much typing every time I wanted to access data values.
Instead of "isinstance(d[k], (list, tuple))", how about using "hasattr(d[k], '__iter__')"? this seems to be more general.
@Eric: Doesn't work, as dicts have __iter__ as well.
This is what I use sometimes, which works a lot nicer:
You still have to type the quotes, but that's hardly a problem.
This is also better, because it's a more general-case solution. Using isinstance should usually be avoided at all costs, as it limits code reusability.
David, your goal "storing configuration data ... using standard Python syntax" is right on. If you like records / structs with named fields, another way of doing this is with namedtuple:
For one-level DotDicts, there's also this one-liner from http://www.norvig.com/python-iaq.html :
(It's possible to merge these approaches, but mttiw moretroublethanitsworth).
@Aaron: Very nice - you're Python Fu is strong ;-)
@denis: Interesting alterative. Python 2.6 certainly is chock full of new code and features!
Thanks for sharing.