functools.partial doesn't work without using named parameter

P

Paddy

Hi, I just found the following oddity where for function fsf1 I am forced to use a named parameter for correct evaluation and was wondering why it doesn't work, yet the example from the docs of wrapping int to create basetwo doesn't need this?
The example:
from functools import partial
basetwo = partial(int, base=2)
basetwo('10010') 18

def fs(f, s): return [f(value) for value in s]
def f1(value): return value * 2
s = [0, 1, 2, 3]
fs(f1, s) [0, 2, 4, 6]

fsf1 = partial(fs, f=f1)
fsf1(s)
Traceback (most recent call last):
File "<pyshell#24>", line 1, in <module>
fsf1(s)
TypeError: fs() got multiple values for keyword argument 'f'

Would someone help?

- Thanks in advance, Paddy.
 
I

Ian Kelly

def fs(f, s): return [f(value) for value in s]

Note that your "fs" is basically equivalent to the "map" builtin,
minus some of the features.
Traceback (most recent call last):
 File "<pyshell#24>", line 1, in <module>
   fsf1(s)
TypeError: fs() got multiple values for keyword argument 'f'
# BUT
fsf1(s=s) [0, 2, 4, 6]

Would someone help?

When you invoke a partial application, it just adds the positional
arguments passed to partial to the positional arguments passed in, and
it adds the keyword arguments passed to partial to the keyword
arguments passed in. Using partial to specify f as a keyword argument
does not change the fact that it is also the first positional
argument. So the call `partial(fs, f=f1)(s)` is equivalent to `fs(s,
f=f1)`. In the latter case, 's' is being passed positionally as the
first argument (i.e. 'f') and 'f1' is being passed by keyword as the
'f' argument, resulting in 'f' being passed in twice. The partial
application is equivalent, so it does the same thing.

The call `partial(fs, f=f1)(s=s)`, however, is equivalent to `fs(f=f1,
s=s)`, which is fine.

Moral of the story: if you pass in an argument by keyword, then the
following arguments must be passed by keyword as well (or not at all),
regardless of whether you're using partial or not.
 
B

Benjamin Kaplan

Hi, I just found the following oddity where for function fsf1 I am forcedto use a named parameter for correct evaluation and was wondering why it doesn't work, yet the example from the docs of wrapping int to create basetwo doesn't need this?
The example:
from functools import partial
basetwo = partial(int, base=2)
basetwo('10010') 18

def fs(f, s): return [f(value) for value in s]
def f1(value): return value * 2
s = [0, 1, 2, 3]
fs(f1, s) [0, 2, 4, 6]

fsf1 = partial(fs, f=f1)
fsf1(s)
Traceback (most recent call last):
 File "<pyshell#24>", line 1, in <module>
   fsf1(s)
TypeError: fs() got multiple values for keyword argument 'f'
# BUT
fsf1(s=s) [0, 2, 4, 6]

Would someone help?

If you hand partial a keyword argument, it treats it as a keyword
argument. If you want to hand it a positional argument, hand it a
positional argument.
[0, 2, 4, 6]
 
S

Steven D'Aprano

Hi, I just found the following oddity where for function fsf1 I am
forced to use a named parameter for correct evaluation and was wondering
why it doesn't work, yet the example from the docs of wrapping int to
create basetwo doesn't need this? The example:

18

When you call basetwo, it gets passed one positional argument and one
keyword argument. The positional arguments are bound from left to right,
as is normal in Python:

int expects to be called like int(a, [base=]b)
int gets passed two arguments: int('10010', base=2)

so all is well. The string '10010' gets bound to the first argument, and
the keyword argument 2 gets bound to the second.


def fs(f, s): return [f(value) for value in s]
def f1(value): return value * 2
s = [0, 1, 2, 3]
fs(f1, s)
[0, 2, 4, 6]

All these f's and s's give me a headache. It's easier to discuss if you
give them distinctive names: spam, ham, eggs are conventions in Python,
foo, bar, baz in some other languages.


Traceback (most recent call last):
File "<pyshell#24>", line 1, in <module>
fsf1(s)
TypeError: fs() got multiple values for keyword argument 'f'


fs expects to be called like fs(f, s). It gets called with one positional
argument, [0,1,2,3], which gets bound to the left-most parameter f, and
one keyword argument, f1, which *also* gets bound to the parameter f
(only by name this time, instead of by position).

[0, 2, 4, 6]

Now, because you're calling fsf1 with keyword argument, fs gets called:

fs(f=f1, s=[0,1,2,3])
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top