2

Have a list of paths + filenames, all starting with the same root_dir. How do I use partial?

Attempt

from os.path import join as path_join
from functools import partial
from tempfile import gettempdir

root_dir = gettempdir()
root_join = partial(path_join, path=root_dir)
root_join('foo')

However this throws an error:

TypeError: join() got multiple values for keyword argument 'path'


Obviously I can write a new function:

root_join = lambda root=root_dir, *a: path_join(root_dir, *a)

However I would like to solve this using partial. Any clue how?

Community
  • 1
  • 1
A T
  • 13,008
  • 21
  • 97
  • 158

2 Answers2

3

Just drop the keyword argument and it will work:

root_join = partial(path_join, root_dir)

Sample output:

>>> print root_join('foo')
/tmp/foo

Explaination

partial() takes both keyword arguments and positional arguments to be passed on to the wrapped function, these are combined with the arguments passed when the resulting partial function is called, so:

partial_func = partial(func, positional_1, positional_2, keyword_1='A', keyword_2='B')
partial_func(positional_3, keywork_2='B')

Is the equivalent of:

func(positional_1, positional_2, positional_3, keyword_1='A', keyword_2='B')

Note that positional arguments are combined in order, the ones from partial first, then the ones supplied at call time. As always they also come before keyword arguments.

This means your code:

root_join = partial(path_join, path=root_dir)
root_join('foo')

Was equivalent to:

path_join('foo', path=root_dir)

Here, 'foo' is given as the path argument to os.path.join(path, *p) positionally, and then there is an attempt to assign path as a keyword argument as well.

Jamie Cockburn
  • 7,379
  • 1
  • 24
  • 37
  • xD, that was simple. Not sure why I thought `partial` only worked with keyword args… – A T Feb 12 '15 at 12:06
2

According to os.path.join()'s definition, the name of first argument for os.path.join() is path. So supplying path parameter as named keyword argument via functools.partial generates an attempt of passing multiple values on same key.

Since functools.partial() also supports supplying of positional argument, you can try this:

>>> import functools
>>> import os.path
>>> root_join = functools.partial(os.path.join, 'root')
>>> root_join('path/to/somewhere')
'root/path/to/somewhere'
wsn
  • 1,097
  • 8
  • 15