carl.hiass
carl.hiass

Reputation: 1764

Doing recursive lookup of a variable

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?

Upvotes: 2

Views: 102

Answers (2)

kaya3
kaya3

Reputation: 51037

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.

Upvotes: 5

aragonnetje6
aragonnetje6

Reputation: 99

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!

Upvotes: -1

Related Questions