Pre-filling arguments with partial

functools · Essentials · 8 min

Callbacks, key functions and configured helpers often need a function with some arguments already decided.

partial(func, *args, **kwargs) returns a new callable that calls func with the given arguments pre-filled, followed by whatever you pass later:

from functools import partial
parse_hex = partial(int, base=16)
parse_hex("ff")   # 255

Positional arguments are prepended; keyword arguments are merged, and keywords supplied at call time override the stored ones.

Typical uses:

  • Passing a configured function where a one-argument callable is expected: map(partial(round, ndigits=2), values), sorted(items, key=partial(distance, origin)).
  • Callbacks in GUIs, threading.Thread(target=...), atexit.register or event systems, where you need to bind arguments now and call later.
  • Specialising general functions: log_error = partial(log, level="ERROR").

A partial object exposes .func, .args and .keywords, and has a readable repr, which a lambda doesn't. It also pickles (if the function does), so it works with multiprocessing where lambdas fail.

Compared with lambda x: int(x, base=16), partial binds values immediately, whereas a lambda looks variables up when called. That avoids the classic late-binding bug in loops that create callbacks.

Example

from functools import partial

parse_hex = partial(int, base=16)
print(parse_hex("ff"), parse_hex("10"))      # 255 16

def power(base, exp):
    return base ** exp
square = partial(power, exp=2)
print(list(map(square, [1, 2, 3])))          # [1, 4, 9]
print(square(3, exp=3))                      # 27  (call-time keyword wins)
print(square.func.__name__, square.keywords) # power {'exp': 2}

lambdas = [lambda: i for i in range(3)]
partials = [partial(int, i) for i in range(3)]
print([f() for f in lambdas])                # [2, 2, 2]  -- late binding
print([f() for f in partials])               # [0, 1, 2]

Remember

  • partial(f, *args, **kw) returns f with some arguments fixed
  • Values are bound immediately, avoiding lambda late-binding bugs
  • Partials are introspectable and picklable

Watch out for

  • Pre-filling a positional argument that isn't the first one; use a keyword argument instead.

Check yourself

  1. p = partial(pow, 2). What is p(5)?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/functools.html#functools.partial