Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass members of a dict to a function

Tags:

python

Suppose I have a function that I do not control that looks something like the following:

def some_func(foo, bar, bas, baz):
    do_something()
    return some_val

Now I want to call this function passing elements from a dict that contains keys that are identical to the arguments of this function. I could do something like:

some_func(foo=mydict['foo'],
          bar=mydict['bar'],
          bas=mydict['bas'],
          baz=mydict['baz'])

Is there some elegant way I could take advantage of the fact that the keys match the parms to do this less verbosely? I know I could pass the whole dict, but let's say I either don't want to or can't change the function to accept a single dict rather than the individual arguments.

Thanks, Jerry

like image 495
zenzic Avatar asked Dec 05 '22 17:12

zenzic


2 Answers

That's what ** argument unpacking is for:

some_func(**mydict)

See also Unpacking argument lists in the Python tutorial.

like image 199
Sven Marnach Avatar answered Dec 27 '22 13:12

Sven Marnach


As Sven notes, you can pass a dict to a function using ** unpacking. But if the dict contains keys that aren't argument names to the target function, this works only if the function you're calling accepts keyword arguments using the ** notation. If it doesn't, you'll get an error.

If the function you're calling doesn't have a **kwargs parameter, the easiest way to handle it is to add one. But if you can't do that, you have a couple approaches:

1) Write a wrapper function:

def wrapper(foo, bar, baz, quux, **kwargs):
    return some_func(foo, bar, baz, quux)

wrapper(**mydict)

2) Write a function to extract just the dict keys you need:

def extract(dikt, keys):
    return dict((k, dikt[k]) for k in keys.split())

some_func(**extract(mydict, "foo bar baz quux"))

3) Write a function that introspects the function you're calling and extracts the keys you need from the dictionary -- basically the same as #2 except you don't have to "repeat yourself" as much.

def call_with_dict(func, dikt):
    func(**dict((k, dikt[k]) for k in 
         func.func_code.co_varnames[:func.func_code.co_argcount]))

call_with_dict(some_func, my_dict)

Note that this doesn't allow you to omit arguments that have default values in the function signature. You could do some additional introspection to permit that (the length of func.func_defaults determines which of the arguments have default values).

This introspection is for Python 2.x, Python 3 and later will probably need some tweaking. For this reason, I prefer one of the first two methods.

PS -- Thanks to Sven for catching my missing ** in my second approach.

like image 39
kindall Avatar answered Dec 27 '22 12:12

kindall