Reputation:
I do not understand the following example, let's say I have these functions:
# python likes
def save(filename, data, **kwargs):
fo = openX(filename, "w", **kwargs) # <- #1
fo.write(data)
fo.close()
# python doesnt like
def save2(filename, data, **kwargs):
fo = openX(filename, "w", kwargs) # <- #2
fo.write(data)
fo.close()
def openX(filename, mode, **kwargs):
#doing something fancy and returning a file object
Why is #1 the right solution and #2 the wrong one? **kwargs
is basically a dict, so if I want to pass down the argument to openX I think the correct way would be without **
and just giving the dict. But Python obviously doesn't like the second one and tells me I gave 3 instead of 2 arguments.
So what's the reason behind this?
Upvotes: 275
Views: 221632
Reputation: 3628
The following code use kwargs
and transfer it to another function:
def myprint( kwargs ):
# default values
a = kwargs.get('a', None)
b = kwargs.get('b', None)
# print both
print('a={}, b={}'.format(a,b))
def mytest( **kwargs ):
myprint( kwargs )
mytest()
mytest(b=2)
yields:
a=None, b=None
a=None, b=2
Upvotes: 4
Reputation: 1672
Expanding on @gecco 's answer, the following is an example that'll show you the difference:
def foo(**kwargs):
for entry in kwargs.items():
print("Key: {}, value: {}".format(entry[0], entry[1]))
# call using normal keys:
foo(a=1, b=2, c=3)
# call using an unpacked dictionary:
foo(**{"a": 1, "b":2, "c":3})
# call using a dictionary fails because the function will think you are
# giving it a positional argument
foo({"a": 1, "b": 2, "c": 3})
# this yields the same error as any other positional argument
foo(3)
foo("string")
Here you can see how unpacking a dictionary works, and why sending an actual dictionary fails
Upvotes: 38
Reputation: 18830
In the second example you provide 3 arguments: filename, mode and a dictionary (kwargs
). But Python expects: 2 formal arguments plus keyword arguments.
By prefixing the dictionary by '**' you unpack the dictionary kwargs
to keywords arguments.
A dictionary (type dict
) is a single variable containing key-value pairs.
"Keyword arguments" are key-value method-parameters.
Any dictionary can by unpacked to keyword arguments by prefixing it with **
during function call.
Upvotes: 261
Reputation: 26968
For #2 args will be only a formal parameter with dict value, but not a keyword type parameter.
If you want to pass a keyword type parameter into a keyword argument You need to specific ** before your dictionary, which means **args
check this out for more detail on using **kw
http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/
Upvotes: 2
Reputation: 43024
The **
syntax tells Python to collect keyword arguments into a dictionary. The save2
is passing it down as a non-keyword argument (a dictionary object). The openX
is not seeing any keyword arguments so the **args
doesn't get used. It's instead getting a third non-keyword argument (the dictionary). To fix that change the definition of the openX
function.
def openX(filename, mode, kwargs):
pass
Upvotes: 16
Reputation: 798526
Because a dictionary is a single value. You need to use keyword expansion if you want to pass it as a group of keyword arguments.
Upvotes: 1