Currying and partial application: pre-filling some of a function's arguments
Lesson 3's make_multiplier example — a function returning a specialized version of itself — turns out to be a specific, named, and very commonly useful pattern, once generalized. This lesson names it and distinguishes it from a closely related, frequently confused idea.
Intermediate
2 min read
Partial application: fixing some arguments now, leaving the rest for later
from functools import partialdef send_request(method, url, body): print(f"{method} {url} with body {body}")post_request = partial(send_request, "POST") # "method" is now permanently fixedpost_request("/users", {"name": "Ana"}) # "POST /users with body {'name': 'Ana'}"
Partial application takes a function and some of its arguments, and returns a new function expecting only the remaining arguments — post_request is send_request with method already locked in as "POST", needing only url and body from here on. This is directly useful for specializing a general-purpose function into several more convenient, narrower ones, without rewriting any of the original logic.
Currying: a stricter, more specific transformation
def curried_add(a): def inner(b): def innermost(c): return a + b + c return innermost return innercurried_add(1)(2)(3) # 6 — each call takes exactly ONE argument
Currying transforms a function taking multiple arguments into a sequence of functions, each taking exactly one argument, chained together — curried_add(1) returns a function, calling that with (2) returns another function, and only the final call with (3) produces the actual result. This is a stricter, more mechanical transformation than partial application: a fully curried function is always called one argument at a time, by construction, whereas partial application can fix any subset of arguments in one step, in whatever grouping is convenient.
Why these two get confused constantly, and the actual distinction that matters
Both produce the same practical outcome in many everyday cases — a specialized function with fewer remaining parameters — which is why the two terms get used loosely and interchangeably in casual conversation. The distinction that actually matters in practice: currying is about a function's fundamental shape (every call takes one argument, full stop, by design from the moment it's defined), while partial application is a technique applied to any ordinary function, fixing whatever arguments are convenient at a given call site, without needing the original function to have been specially designed for it.
Where this shows up in ordinary, non-exotic code
# A "config" dict pattern, partially applied:from functools import partialdef log(level, message): print(f"[{level}] {message}")warn = partial(log, "WARNING")error = partial(log, "ERROR")warn("disk space low") # [WARNING] disk space lowerror("connection lost") # [ERROR] connection lost
This warn/error pattern — specializing one general function into several convenient, named ones — appears constantly in real code, often without anyone explicitly naming it as "partial application": a logging library's level-specific helpers, an HTTP client's method-specific shortcuts (client.get, client.post as specializations of a general client.request), a configured database connection function pre-filled with connection details. Recognizing the pattern by name makes it a deliberate tool to reach for, rather than something reinvented slightly differently each time it's needed.