A partial function is a regular function with some of its arguments already filled in. You hand it a few arguments now, and the result is a new callable that only needs the rest. Python ships this idea as functools.partial, and it shows up in callbacks with bound state, simplified signatures for repeat calls, and configured versions of general helpers. This chapter covers how partial works, when to use it instead of a lambda, how it composes with map and filter, the related partialmethod for class methods, and a few practical caveats.
partial Doesfunctools.partial(func, *args, **kwargs) returns a new callable. Calling that new thing is like calling func with the saved arguments plus whatever the caller adds. The original function is untouched; nothing global changes. You just get back a second callable that has been pre-loaded with some inputs.
partial(shipping_cost, zone=1) produces a new callable. Calling zone_1_shipping(2.5) is the same as calling shipping_cost(2.5, zone=1). The zone=1 is baked in once; every caller after that only supplies the remaining arguments. The original shipping_cost is still around and works exactly as before. The partial is an additional callable, not a replacement.
You can bind positional arguments too. They fill in from the left.
partial(price_with_tax, 99.00) fills in price with 99.00. The new callable accepts the rest in order: first tax_rate, then discount. Each call to flat_99 reuses the bound price without you having to type it again.
Positional and keyword binding can be mixed. Whatever you save in the partial call is saved; whatever you supply at call time is added on.
alice_order has customer="Alice" (positional) and status="placed" (keyword) baked in. The caller adds item and quantity, and may override status if they want. The third call passes status="shipped" at call time, which overrides the saved keyword default.
A partial is a regular callable. It can be passed anywhere a function is expected, stored in a variable or a list, returned from another function, or used as an argument to another partial. The thing it returns is callable just like the original.
Each call through a partial does a small amount of extra work: building the merged argument tuple and dict, then calling the underlying function. The overhead is a few microseconds at most, which is invisible for functions that do any real work and only worth thinking about in tight loops over trivial calls.
partial Instead of Just Calling the FunctionYou could always write shipping_cost(weight, zone=1) at every call site. partial is useful when you want to save that decision, not repeat it. Three common cases:
Callbacks and higher-order functions. When you hand a function to sorted, map, filter, or any framework that calls it back later, the receiver decides what arguments to pass. If your function needs an extra argument the receiver doesn't know about, pre-bind that argument before handing the function over. partial does this in one line.
sorted expects key to be a one-argument function that takes a product and returns the sort value. sort_key takes two arguments. partial(sort_key, by="price") produces a one-argument function with by="price" already supplied, which is exactly what sorted wants. The same sort_key is reused with a different binding for ratings.
Simplified signatures for repeat callers. Long-signature functions are fine when each call uses different arguments. When every call in a module uses the same value for one parameter, repeating it on every line adds noise. Bind it once.
The timestamp and customer ID are constant for the duration of one request, so binding them once with partial keeps the call sites focused on what's actually changing. The result reads better and there's one place to fix if the bound value needs to change.
Pre-configured versions of general helpers. A general-purpose function with several knobs is more reusable, but you often want specific named versions for common cases. partial builds those named versions without copying code.
One general formatter, three specific ones built from it. No duplication of the formatting logic; each currency version is one line.
Both partial and lambda can solve the same problem: turn a multi-argument function into a callable that has some arguments pre-filled. The difference is in style and a few small features.
Both produce a one-argument callable that applies a California sales tax. The behavior is identical; the choice is mostly about style and a couple of practical concerns.
The case for partial:
partial says "this is a function with some arguments pre-filled". A lambda hides that intent inside its body.partial objects are introspectable. You can read .func, .args, and .keywords to see what's bound. That's useful for debugging and for code that needs to inspect callbacks.partial is picklable if the underlying function is picklable. A lambda is not picklable. This matters for multiprocessing, joblib, and other tools that serialize callables across processes.The case for lambda:
partial only binds.lambda x: f(x, 0.08) is shorter than importing partial and writing two lines.partial lives in functools.A rule of thumb: if all you're doing is fixing some arguments of a function you've already named, use partial. If you're also computing or rearranging, use a lambda. If you'll serialize the result for multiprocessing or save it to disk, use partial (or a regular def).
alice_order.func is the original make_order. alice_order.args is the tuple of bound positional arguments. alice_order.keywords is the dict of bound keyword arguments. A lambda has none of these; it's just a callable with no record of what it captured.
map, filter, and sortedpartial fits naturally in code that passes functions around. map, filter, and sorted all take a callable and call it for each element of an iterable. When the function you want needs an extra argument they don't supply, partial bridges the gap.
map calls with_8_pct once per price. The rate=0.08 is already bound, so each call only needs the one price. The result is the same as [add_tax(p, 0.08) for p in prices], and which one reads better depends on the codebase's conventions. The partial plus map version reuses with_8_pct elsewhere if needed; the comprehension repeats the rate inline.
filter works the same way. The predicate needs to be a one-argument function returning a boolean. If your predicate needs a threshold (or any other configuration), bind it with partial.
filter calls partial(under_budget, max_price=15.00) once per product. Each call passes the product as the first argument; max_price=15.00 is already bound. Products with prices over 15 are filtered out.
The same pattern works for sorted's key argument, as shown earlier. Anywhere a one-argument callable is required and your function needs more, partial is the standard fix.
Currying and partial application are related but not the same. Both let you "supply arguments later", but they're shaped differently and they come from different traditions.
Currying transforms a function f(a, b, c) into a chain of one-argument calls: f(a)(b)(c). Each call returns a function that expects the next argument. You can't supply two arguments at the same time; you supply one at a time, and the function eats them one at a time.
Partial application lets you supply any number of arguments now and get back a callable that needs the rest. You can pre-bind one, two, or all of them in a single step, and you can choose positional or keyword bindings.
Python's functools.partial is partial application, not currying. The short version: partial is more flexible (bind whatever you want, in any order) but doesn't compose as cleanly in functional-style pipelines. Curried functions compose naturally because every intermediate step is itself a function expecting one argument, which fits patterns like f >> g >> h.
In day-to-day Python code, partial is far more common than true currying. The language doesn't encourage one-argument-at-a-time chains, and partial provides the practical benefit (specializing a function) without the FP-language style.
partialmethod: Partial Application on Methodsfunctools.partialmethod is the cousin of partial for use inside class bodies. It binds arguments to a method instead of a free function, so the bound arguments come after self rather than before.
partialmethod(set_status, "shipped") produces a method that, when called as order.mark_shipped(), calls set_status(order, "shipped"). The self binding still works the normal way (it's filled in when you access the method on an instance), and the extra argument is bound by partialmethod. This pattern fits when you want named methods that are thin wrappers around one general-purpose method.
Using a plain partial here would not work, because partial doesn't know about the descriptor protocol that classes use to bind self. partialmethod is the version that does.
A partialmethod call is one extra function call over the plain method call, which is negligible. The benefit is fewer lines of repetitive method definitions for cases like state-machine transitions or fixed-argument variants.
A few details are worth knowing before using partial heavily. None are deep, but knowing them in advance saves time.
`partial` of a `partial` works, and the bindings compose. You can wrap a partial in another partial. The bindings accumulate from the outside in.
zone_3 binds zone=3. zone_3_express is built from zone_3 and binds express=True on top of it. The result is a callable that needs only the weight. The technique is occasionally useful, but stacking three or four partials is usually clearer as a small wrapper function.
Positional binding fills from the left only. To bind a later positional argument without binding the earlier ones, use a keyword. partial(f, x=10) works for any function whose parameter list lets x be supplied by keyword. partial(f, 10) always binds the first positional argument, not whichever argument you meant.
There's no syntax for "skip the first positional and bind the second". Use a keyword when the parameter you want to bind isn't the leftmost one.
Don't confuse the variable name `partial` with `functools.partial`. It's easy to shadow partial with a local variable by accident. If a function called partial is suddenly raising TypeError, check whether something else has been assigned to that name in scope. The convention in most codebases is to import partial directly and avoid reusing the name.
The fix is to use a different name (partial_total, discount_partial, etc.) for any variable that would otherwise collide.
Picklability follows the wrapped function. A partial is picklable if and only if its func, args, and keywords are all picklable. Named module-level functions are picklable. Lambdas, nested functions, and methods bound to dynamically-created instances are usually not. When using partial with multiprocessing.Pool.map and seeing a "PicklingError", check what's being wrapped.
The second partial wraps a function defined inside make_pricer. That inner function isn't reachable by name from the top level, so pickle can't serialize it. partial doesn't change this; whatever rules apply to the underlying function apply to the partial too.
10 quizzes