I was kind of wondering what ways are out there to elegantly expand '$name' identifiers in nested dictionary value. The problem arose when I wanted to include that kind of functionality to dicts read from yaml files such that:
def func(input): # do something return output where: input = {'firstname': 'John', 'lastname': 'Smith', 'src': 'c:/tmp/ file', 'dir': {'path': '$src', 'fullname': '$firstname $lastname'}} output = {'firstname': 'John', 'lastname': 'Smith', 'src': 'c:/tmp/ file', 'dir': {'path': 'c:/tmp/file', 'fullname': 'John Smith'}} Doing this substitution easy done when you have a flat dict, but when they got nested, I had to resort to an undoubtedly ugly function with two recursive passes and obvious deficiencies. Is there a better way to do this? Thanks for any help... AK <code follows> # test_recurse.py from string import Template from pprint import pprint def expand(dikt): ''' >>> d = expand2({'firstname': 'John', 'lastname': 'Smith', 'fullname': '$firstname $lastname'}) >>> d == {'lastname': 'Smith', 'fullname': 'John Smith', 'firstname': 'John'} True ''' subs = {} for key, value in dikt.items(): if '$' in value: subs[key] = Template(value).substitute(dikt) dikt.update(subs) return dikt dikt = {'firstname': 'John', 'lastname': 'Smith', 'fullname': '$firstname $lastname'} #~ print expand(dikt) d1 = {'firstname': 'John', 'lastname': 'Smith', 'dir': {'fullname': '$firstname $lastname'} } d2 = {'firstname': 'John', 'lastname': 'Smith', 'src': 'c:/tmp/file', 'dir': {'fullname': '$firstname $lastname', 'path': '$src'} } def rexpand(dikt): subs = {} names = {} # pass 1 def recurse(_, sourceDict): for key, value in sourceDict.items(): if isinstance(value, dict): recurse({}, value) elif '$' in value: subs[key] = value else: names[key] = value recurse({}, dikt) print 'subs', subs print 'names', names print 'dikt (before):', dikt for key, value in subs.items(): subs[key] = Template(value).substitute(names) # ----------------------------------------------------- # pass 2 output = {} def substitute(targetDict, sourceDict): for key, value in sourceDict.items(): if isinstance(value, dict): new_target = targetDict.setdefault(key, {}) substitute(new_target, value) else: targetDict[key] = Template(value).substitute(names) substitute(output, dikt) print 'output:', output return output rexpand(d2) -- http://mail.python.org/mailman/listinfo/python-list