recursively expanding $references in dictionaries

Alia Khouri alia_khouri at yahoo.com
Sun Jul 22 18:47:53 EDT 2007


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)




More information about the Python-list mailing list