518

I'd like to call a function in python using a dictionary with matching key-value pairs for the parameters.

Here is some code:

d = dict(param='test')

def f(param):
    print(param)

f(d)

This prints {'param': 'test'} but I'd like it to just print test.

I'd like it to work similarly for more parameters:

d = dict(p1=1, p2=2)
def f2(p1, p2):
    print(p1, p2)
f2(d)

Is this possible?

0

4 Answers 4

753

Figured it out for myself in the end. It is simple, I was just missing the ** operator to unpack the dictionary

So my example becomes:

d = dict(p1=1, p2=2)
def f2(p1,p2):
    print(p1, p2)
f2(**d)
Sign up to request clarification or add additional context in comments.

12 Comments

It's worth noting that you can also unpack lists to positional arguments: f2(*[1,2])
"dereference": the usual term, in this Python context, is "unpack". :)
This is great, just used it with argparse/__dict__ to make it really easy to do command line argument parsing directly into options for a class object.
what is the reason we would want to unpack a dictionary when passing it as an argument to a function?
@MonaJalal To forward it to another function that takes **kwargs, for instance; it will expect an unpacked list of arguments, not just a dictionary of arguments.
|
228
In[1]: def myfunc(a=1, b=2):
In[2]:    print(a, b)

In[3]: mydict = {'a': 100, 'b': 200}

In[4]: myfunc(**mydict)
100 200

A few extra details that might be helpful to know (questions I had after reading this and went and tested):

  1. The function can have parameters that are not included in the dictionary
  2. You can not override a function parameter that is already in the dictionary
  3. The dictionary can not have values that aren't in the function.

Examples:

Number 1: The function can have parameters that are not included in the dictionary

In[5]: mydict = {'a': 100}
In[6]: myfunc(**mydict)
100 2

Number 2: You can not override a function parameter that is already in the dictionary

In[7]: mydict = {'a': 100, 'b': 200}
In[8]: myfunc(a=3, **mydict)

TypeError: myfunc() got multiple values for keyword argument 'a'

Number 3: The dictionary can not have values that aren't in the function.

In[9]:  mydict = {'a': 100, 'b': 200, 'c': 300}
In[10]: myfunc(**mydict)

TypeError: myfunc() got an unexpected keyword argument 'c'

How to use a dictionary with more keys than function arguments:

A solution to #3, above, is to accept (and ignore) additional kwargs in your function (note, by convention _ is a variable name used for something being discarded, though technically it's just a valid variable name to Python):

In[11]: def myfunc2(a=None, **_):
In[12]:    print(a)

In[13]: mydict = {'a': 100, 'b': 200, 'c': 300}

In[14]: myfunc2(**mydict)
100

Another option is to filter the dictionary based on the keyword arguments available in the function:

In[15]: import inspect
In[16]: mydict = {'a': 100, 'b': 200, 'c': 300}
In[17]: filtered_mydict = {k: v for k, v in mydict.items() if k in [p.name for p in inspect.signature(myfunc).parameters.values()]}
In[18]: myfunc(**filtered_mydict)
100 200

Example with both positional and keyword arguments:

Notice further than you can use positional arguments and lists or tuples in effectively the same way as kwargs, here's a more advanced example incorporating both positional and keyword args:

In[19]: def myfunc3(a, *posargs, b=2, **kwargs):
In[20]:    print(a, b)
In[21]:    print(posargs)
In[22]:    print(kwargs)

In[23]: mylist = [10, 20, 30]
In[24]: mydict = {'b': 200, 'c': 300}

In[25]: myfunc3(*mylist, **mydict)
10 200
(20, 30)
{'c': 300}

4 Comments

Using unpacking with print.format is particularly useful. eg: 'hello {greeting} {name}'.format( **{'name': 'Andrew', 'greeting': 'Mr'})
Old question but still very relevant. Thanks for the detailed response. Do you know any ways to work around case 3? Meaning pythonically map the items of the dictionary to the function parameters, when there are more items in the dictionary than there are parameters?
@spencer a solution has been added to the answer.
@Martlark Using template strings, it will be like: subst_dict = {'name': 'Andrew','greeting': 'Mr.'} title = 'hello $greeting $name' formatted_title = Template(title).substitute(**subst_dict)
38

In python, this is called "unpacking", and you can find a bit about it in the tutorial. The documentation of it sucks, I agree, especially because of how fantasically useful it is.

5 Comments

It is better to copy the relevant content of the link into your answer, rather than relying on the link surviving until the end of time.
@Richard that's a deep philosophical opinion about the web, with which I couldn't disagree more heartily! Alas, I lack the space in this here margin to share my wonderful proof...
@llimllib, I shall have to ask Dr. Wiles then!
"The documentation of it sucks", unless your problem is to print "This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised" using a dictionary. In that case it's perfect.
llimllib fwiw the official StackOverflow suggestion now is as @Richard suggests: Always quote the most relevant part of an important link, in case the external resource is unreachable or goes permanently offline
6

Here ya go - works just any other iterable:

d = {'param' : 'test'}

def f(dictionary):
    for key in dictionary:
        print key

f(d)

4 Comments

It seems that people are downvoting this as it answered the original question, not the rephrased question. I suggest just removing this post now.
@dotancohen no it was never correct, it fails the second block of code that was always with the question. It took it too literally, the print was an example.
It does answer the question though, it just doesn't do it via dictionary unpacking. His approach is perfectly valid based on the question posted.
No, it doesn't because the parameter of f is "dictionary" and there is no corresponding name in the dictionary. The other answers demonstrate a correct understanding.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.