Passing a dictionary to a function as keyword parameters

283,322

Solution 1

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)

Solution 2

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}

Solution 3

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.

Solution 4

Here ya go - works just any other iterable:

d = {'param' : 'test'}

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

f(d)
Share:
283,322
Dave Hillier
Author by

Dave Hillier

Technical leader, experienced with a wide range of platforms, technologies and industries. Accomplished in designing, developing and maintaining real-time distributed systems in both games and finance. A passionate and skilled advocate of Agile methods. Used XP development practices since the start of my career in 2002. Successfully delivered many projects as Scrum Master. Coached teams in their adoption of Agile processes and technical practices.

Updated on December 14, 2021

Comments

  • Dave Hillier
    Dave Hillier over 2 years

    I'd like to call a function in python using a dictionary.

    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?

  • nullException
    nullException over 15 years
    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
  • Matthew Trevor
    Matthew Trevor over 15 years
    It's worth noting that you can also unpack lists to positional arguments: f2(*[1,2])
  • mipadi
    mipadi almost 15 years
    "dereference": the usual term, in this Python context, is "unpack". :)
  • Horus
    Horus almost 12 years
    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.
  • dotancohen
    dotancohen over 10 years
    It seems that people are downvoting this as it answered the original question, not the rephrased question. I suggest just removing this post now.
  • Dave Hillier
    Dave Hillier about 10 years
    @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.
  • Richard
    Richard almost 10 years
    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.
  • llimllib
    llimllib almost 10 years
    @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...
  • Richard
    Richard almost 10 years
    @llimllib, I shall have to ask Dr. Wiles then!
  • Mona Jalal
    Mona Jalal almost 8 years
    what is the reason we would want to unpack a dictionary when passing it as an argument to a function?
  • Thomas
    Thomas over 7 years
    @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.
  • Natecat
    Natecat over 7 years
    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.
  • Brōtsyorfuzthrāx
    Brōtsyorfuzthrāx over 6 years
    @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).
  • Martlark
    Martlark over 6 years
    Using unpacking with print.format is particularly useful. eg: 'hello {greeting} {name}'.format( **{'name': 'Andrew', 'greeting': 'Mr'})
  • spencer
    spencer about 5 years
    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?
  • David Parks
    David Parks about 5 years
    @spencer a solution has been added to the answer.
  • R__raki__
    R__raki__ almost 5 years
    @Dave, how that param is converted into 'param' , i mean how a variable name is converted into string :-))
  • Dave Hillier
    Dave Hillier almost 5 years
    is that a question @user2728397?
  • R__raki__
    R__raki__ almost 5 years
    @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 .
  • mins
    mins over 3 years
    "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.
  • ali14
    ali14 about 3 years
    @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)
  • Gulzar
    Gulzar about 3 years
    @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.
  • Dave Hillier
    Dave Hillier about 3 years
    Pretty certain someone else edited my original title regardless. At least this question is now protected.
  • Technophile
    Technophile over 2 years
    @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().