12

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?

GreenMatt
  • 18,244
  • 7
  • 53
  • 79
motam79
  • 3,542
  • 5
  • 34
  • 60
  • 1
    When posting a question about code that is throwing an error, please include the Traceback. – wwii Aug 16 '16 at 13:25
  • 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? – wwii Aug 16 '16 at 14:15

3 Answers3

12

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):
Community
  • 1
  • 1
DeepSpace
  • 78,697
  • 11
  • 109
  • 154
  • 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. – Ben Farmer Nov 14 '19 at 15:56
  • @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). – DeepSpace Nov 14 '19 at 16:02
  • 7
    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. – Ben Farmer Nov 14 '19 at 16:08
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

Alexandre V.
  • 416
  • 5
  • 9
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)]
mic4ael
  • 7,974
  • 3
  • 29
  • 42