python partial with keyword arguments
Asked Answered
S

3

14

I have a function that takes 3 keyword parameters. It has default values for x and y and I would like to call the function for different values of z using map. When I run the code below I get the following error:

foo() got multiple values for keyword argument 'x'

def foo(x =1, y = 2, z = 3):
    print 'x:%d, y:%d, z:%d'%(x, y, z)


if __name__ == '__main__':
    f1 = functools.partial(foo, x= 0, y = -6)
    zz = range(10)
    res = map(f1, zz)

Is there a Pythonic way to solve this problem?

Seventh answered 16/8, 2016 at 13:13 Comment(2)
When posting a question about code that is throwing an error, please include the Traceback.Appropriate
Will you be creating partial's for different combinations of the arguments than what you show in the example? Are you looking for a generic solution where you won't have to specify the missing arguments' name in the partial call signature?Appropriate
U
13

map(f1, zz) tries to call the function f1 on every element in zz, but it doesn't know with which arguments to do it. partial redefined foo with x=0 but map will try to reassign x because it uses positional arguments.

To counter this you can either use a simple list comprehension as in @mic4ael's answer, or define a lambda inside the map:

res = map(lambda z: f1(z=z), zz)

Another solution would be to change the order of the arguments in the function's signature:

def foo(z=3, x=1, y=2):
Unbeknown answered 16/8, 2016 at 13:27 Comment(3)
Isn't this a serious flaw in 'partial'? What good is it if the output function doesn't behave like a function should? I would expect it should behave as a function with only one positional/keyword argument.Biotic
@BenFarmer The output function does behave as a function should. The "problem" is that map does not know that one of the arguments was already provided (and it can't really, if you want to keep its implementation as general as possible).Unbeknown
I mean it doesn't behave as a function that doesn't have the fixed argument at all, which seems to be the whole point of partial. We aren't trying to set a new "default" for an argument, it is supposed to be fixed and removed from the picture. Map shouldn't have to worry about this because it just shouldn't even know that 'x' or 'y' ever existed. It should just see a function with 'z' as the only argument.Biotic
S
2

You may obtain the equivalent result you expect without using partial:

def foo(x =1, y = 2, z = 3):
    print 'x:%d, y:%d, z:%d'%(x, y, z)


if __name__ == '__main__':
    f1 = lambda z: foo(x=0, y=-6, z=z)
    zz = range(10)
    res = map(f1, zz)

Please see this link to have a better understanding: functools.partial wants to use a positional argument as a keyword argument

Siusan answered 19/12, 2019 at 15:23 Comment(0)
M
0

When trying to call res = map(f1, zz) internally it actually looks like [f1(i) for i in range(10). As you can see you call f1 function with only one positional argument (in this case f1(x=i)). A possible solution would be to do something like this

res = [f1(z=i) for i in range(10)]
Manado answered 16/8, 2016 at 13:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.