"unpacking" a passed dictionary into the function's name space in Python?

56,928

Solution 1

You can always pass a dictionary as an argument to a function. For instance,

dict = {'a':1, 'b':2}
def myFunc(a=0, b=0, c=0):
    print(a,b,c)
myFunc(**dict)

Solution 2

If you like d.variable syntax better than d['variable'], you can wrap the dictionary in an almost trivial "bunch" object such as this:

class Bunch:
    def __init__(self, **kw):
        self.__dict__.update(kw)

It doesn't exactly bring dictionary contents into the local namespace, but comes close if you use short names for the objects.

Solution 3

Assuming all keys in your dictionary qualify to be identifiers, You can simply do this:

adict = { 'x' : 'I am x', 'y' : ' I am y' }
for key in  adict.keys():
  exec(key + " = adict['" + key + "']")
blah(x)
blah(y)

Solution 4

This is similar to your decorator idea, but it is a bit more general, as it allows you to pass an arbitrary number of dicts to foo, and the decorator does not have to know anything about the keys in the dicts or the order of the arguments when calling the underlying foo function.

#!/usr/bin/env python
d1 = {'x':1,'y':2}
d2 = {'a':3,'b':4}

def unpack_dicts(f):
    def f_new(*dicts):
        new_dict={}
        for d in dicts:
            new_dict.update(d)
        return f(**new_dict)
    return f_new

@unpack_dicts
def foo(x,y,a,b):
    print x,y,a,b

foo(d1,d2)
# 1 2 3 4

Solution 5

Here's a unpacking method in a one liner:

x,y = (lambda a,b,**_: (a,b))(**{'a':'x', 'b':'y', 'c': 'z'})

the lambda args a and b are the keys I want to unpack into x and y in that order. **_ is there to ignore any other keys in the dictionary, i.e. c.

Share:
56,928
Andrew Wagner
Author by

Andrew Wagner

I'm currently a Post-Doc researcher at the University of Leuven, Belgium working on launching systems for flying wind turbines.

Updated on January 09, 2020

Comments

  • Andrew Wagner
    Andrew Wagner over 4 years

    In the work I do, I often have parameters that I need to group into subsets for convenience:

    d1 = {'x':1,'y':2}
    d2 = {'a':3,'b':4}
    

    I do this by passing in multiple dictionaries. Most of the time I use the passed dictionary directly, i.e.:

    def f(d1,d2):
        for k in d1:
            blah( d1[k] )
    

    In some functions I need to access the variables directly, and things become cumbersome; I really want those variables in the local name space. I want to be able to do something like:

    def f(d1,d2)
        locals().update(d1)
        blah(x)
        blah(y)    
    

    but the updates to the dictionary that locals() returns aren't guaranteed to actually update the namespace.

    Here's the obvious manual way:

    def f(d1,d2):
        x,y,a,b = d1['x'],d1['y'],d2['a'],d2['b']
        blah(x)
        return {'x':x,'y':y}, {'a':a,'b':b}
    

    This results in three repetitions of the parameter list per function. This can be automated with a decorator:

    def unpack_and_repack(f):
        def f_new(d1, d2):
            x,y,a,b = f(d1['x'],d1['y'],d2['a'],d3['b'])
            return {'x':x,'y':y}, {'a':a,'b':b}
        return f_new
    @unpack
    def f(x,y,a,b):
        blah(x)
        blah(y)
        return x,y,a,b
    

    This results in three repetitions for the decorator, plus two per function, so it's better if you have a lot of functions.

    Is there a better way? Maybe something using eval? Thanks!

  • ddaa
    ddaa over 14 years
    Although that might technically work, this is a terrible idea for readability. Also I suspect it will not actually work correctly because references to bar will not be compiled as local variable access in foo.
  • Jouni K. Seppänen
    Jouni K. Seppänen over 14 years
    To bypass that particular bit of "deep magic" in variable lookup, add something like exec "" to the function to keep the Python compiler from optimizing the function. Then I think even locals().update will work. Not that I would recommend this technique.
  • Andrew Wagner
    Andrew Wagner over 14 years
    That's an improvement... But you still would have to manually repack the return values of the function. Too bad there aren't named return parameters... If I were qualified to implement this, I'd consider writing a PEP. Thanks!
  • Andrew Wagner
    Andrew Wagner over 14 years
    Thanks, but I'm aware of that. I'm trying to abstract away the argument lists for the purpose of generalizing the code.
  • Zitrax
    Zitrax over 6 years
    IDE's will not understand that x and y are valid variables after this.
  • Sanjay Manohar
    Sanjay Manohar over 4 years
    Can you explain how this is this supposed to work? It looks as though it might return only the first key-value pair?