475

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?

Dave Hillier
  • 18,105
  • 9
  • 43
  • 87

4 Answers4

709

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)
Richard
  • 56,349
  • 34
  • 180
  • 251
Dave Hillier
  • 18,105
  • 9
  • 43
  • 87
  • 90
    if you'd want this to help others, you should rephrase your question: the problem wasn't passing a dictionary, what you wanted was turning a dict into keyword parameters – Javier Dec 02 '08 at 17:28
  • 17
    It's worth noting that you can also unpack lists to positional arguments: f2(*[1,2]) – Matthew Trevor Dec 02 '08 at 23:44
  • 12
    "dereference": the usual term, in this Python context, is "unpack". :) – mipadi Jul 02 '09 at 18:05
  • 3
    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. – Horus Jun 14 '12 at 03:47
  • 1
    what is the reason we would want to unpack a dictionary when passing it as an argument to a function? – Mona Jalal May 30 '16 at 06:07
  • 1
    @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. – Thomas Oct 08 '16 at 10:47
  • 1
    @MonaJalal It can be useful if you want to do such as `ast.literal_eval(someText)`, which could convert `someText` into a dictionary; then you could unpack it into a method (and `someText` could be previously parsed to match the keywords in the method better, if you want alias keywords or such). So, that would be one way to allow for data entry (done to trigger methods), where it doesn't require multiple forms, boxes, mouse-clicks, etc. in a fashion without editing `.py` files themselves all the time (and it's one example of possibly countless where unpacking a dictionary could be useful). – Brōtsyorfuzthrāx Aug 18 '17 at 05:38
  • @Dave, how that `param` is converted into `'param'` , i mean how a variable name is converted into string :-)) – R__raki__ Jul 19 '19 at 11:18
  • 1
    is that a question @user2728397? – Dave Hillier Jul 19 '19 at 13:12
  • 1
    @DaveHillier yes, just curios , how internally a variable name is changed into string, this also happens when you pass a dictionary to a function call , all the keys which are strings are converted back into the variable names , how python internally does that, can it be done in the python interpreter or is it a just internal manipulation . – R__raki__ Jul 20 '19 at 07:20
  • 1
    @Javier The *solution* was unpacking the dict. The *question* was passing dict as named params. Thus, future users, would still prefer the original, as it is more Googlable. – Gulzar Apr 13 '21 at 11:25
  • Pretty certain someone else edited my original title regardless. At least this question is now protected. – Dave Hillier Apr 13 '21 at 12:49
  • @MonaJalal want to do it when calling a function that takes different named arguments depending on what configuration you want. Passing in e.g. `None` for any unused values will override any default values. A specific example is asyncssh.connect(). – Technophile Oct 21 '21 at 18:48
215
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}
David Parks
  • 30,789
  • 47
  • 185
  • 328
  • 6
    Using unpacking with print.format is particularly useful. eg: `'hello {greeting} {name}'.format( **{'name': 'Andrew', 'greeting': 'Mr'})` – Martlark Nov 02 '17 at 00:12
  • 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 Apr 25 '19 at 14:22
  • 3
    @spencer a solution has been added to the answer. – David Parks May 02 '19 at 17:37
  • 2
    @Martlark Using [template strings](https://realpython.com/python-string-formatting/#4-template-strings-standard-library), it will be like: `subst_dict = {'name': 'Andrew','greeting': 'Mr.'} title = 'hello $greeting $name' formatted_title = Template(title).substitute(**subst_dict)` – ali14 Feb 15 '21 at 17:20
36

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.

llimllib
  • 3,642
  • 1
  • 29
  • 29
  • 34
    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 Jul 26 '14 at 20:06
  • 5
    @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 Jul 28 '14 at 16:02
  • 1
    @llimllib, I shall have to ask Dr. Wiles then! – Richard Jul 28 '14 at 19:49
  • "*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. – mins Jan 31 '21 at 15:00
  • llimllib fwiw [the official StackOverflow suggestion](https://stackoverflow.com/help/how-to-answer) 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` – dantiston May 08 '23 at 16:41
6

Here ya go - works just any other iterable:

d = {'param' : 'test'}

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

f(d)
Patrick Harrington
  • 47,416
  • 5
  • 23
  • 20
  • 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 Dec 10 '13 at 08:00
  • @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. – Dave Hillier Feb 26 '14 at 21:28
  • 1
    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. – Natecat Nov 30 '16 at 01:19
  • 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. – Dave Hillier Sep 22 '22 at 13:39