Doing recursive lookup of a variable

Viewed 64

I have a list of dictionaries to emulate a stack of environments for an interpreter. For example, in the first environment I might have the value a=4 defined, and in the second environment I might have b=3 defined, and so If I do env_get(a) + env_get(b) I should get 7.

Here is what I currently have for this:

def env_get(self, k, env=None):
    v = None
    for env in reversed(self.envs): # as the "pop" is the equivalent of env[-1]
        v = env.get(k)
        if v: break
    if not v: raise AttributeError("Env does not contain '%s'" % k)
    return v

I was wondering if this could be condensed into a single recursive def such as the following:

envs=[{'a':4},{'b':3}]
def get_env_recursive(k, env_stack=None):
    if not env_stack: raise AttributeError("Env does not contain '%s'" % k)
    return env_stack[-1].get(k) or get_env_recursive(k, env_stack[:-1])
>>> get_env_recursive('a',envs)+get_env_recursive('b',envs)
7

Or to go crazy on a one-liner could do something like:

>>> getr=lambda k,env_stack: env_stack[-1].get(k) or getr(k, env_stack[:-1]) if len(env_stack)>1 else env_stack[0][k]
>>> getr('a',envs)+getr('b',envs)
# 7

Is there a better way I could do the above pattern?

2 Answers

The ChainMap class from the collections module is designed for this purpose (docs link). If env_stack is an instance of ChainMap then you can simply write env_stack[k] to get the value for the key k from whichever dictionary nearest the top of the stack has that key.

For example, you can create a stack of two dictionaries like ChainMap({'a': 4}, {'b': 3}), where the first one is on the top of the stack. More usage examples can be found in the documentation.

The pattern seems fine, though I'd recommend removing the default value for env_stack, as it will always cause an exception. It doesn't really make sense to have a default argument which is always invalid, so unless there's a reason to keep it, I'd remove it.

Good work!

Related