[python] pass **kwargs argument to another function with **kwargs

I do not understand the following example, lets 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 doesnt like the second one and tells me I gave 3 instead of 2 arguments. So whats the reason behind this?

This question is related to python

The answer is


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


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/


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

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.