Currying in Python — Core Concepts
What Is Currying?
Currying transforms a function that takes multiple arguments into a sequence of functions that each take a single argument. Named after mathematician Haskell Curry, it’s a core concept in functional programming.
Given a function f(a, b, c), currying produces f(a)(b)(c) — three function calls, each consuming one argument.
Currying vs Partial Application
These terms get confused constantly, but they’re different:
- Currying converts
f(a, b, c)intof(a)(b)(c)— always one argument at a time. - Partial application fixes some arguments and returns a function that takes the rest:
partial(f, a)gives youg(b, c).
In practice, Python’s toolz.curry blends both: you can provide one argument or several, and it returns a new function waiting for whatever’s left.
How to Curry in Python
Manual Currying with Closures
def multiply(x):
def inner(y):
return x * y
return inner
double = multiply(2)
triple = multiply(3)
double(10) # 20
triple(10) # 30
This works for two arguments but gets tedious for functions with more.
Using functools.partial
from functools import partial
def power(base, exponent):
return base ** exponent
square = partial(power, exponent=2)
cube = partial(power, exponent=3)
partial is not true currying — it’s partial application — but it solves many of the same problems.
Using toolz.curry
from toolz import curry
@curry
def add(x, y, z):
return x + y + z
add(1)(2)(3) # 6 — true currying
add(1, 2)(3) # 6 — partial application
add(1)(2, 3) # 6 — mixed
add(1, 2, 3) # 6 — normal call
The @curry decorator inspects the function’s signature and waits until all required arguments are supplied before executing.
Common Misconception
“Currying is just a fancy way to set defaults.” Defaults are fixed at definition time. Currying creates new specialized functions at runtime based on context. A curried format_price(currency)(amount) can produce format_usd, format_eur, and format_gbp — each a reusable function, not just a value with a default.
When Currying Shines
- Pipeline stages — Curried functions slot naturally into
map(),filter(), and composition chains without writing lambdas. - Configuration — Curry a general function with environment-specific settings early, then use the specialized version everywhere.
- Event handlers — Create handlers with pre-baked context:
on_click = handle_event("button_save"). - Testing — Curry a database function with a mock connection to get a pure function for unit tests.
When to Avoid It
- Functions with complex keyword arguments or
*args/**kwargsdon’t curry well. - Overusing currying in a codebase where teammates aren’t familiar with it creates confusion.
- If you’re only fixing one argument once,
partialis simpler and more explicit.
One Thing to Remember
Currying turns one function into a chain of specialized functions — give it arguments one at a time and get reusable building blocks at every step.
See Also
- Python Function Composition Discover how snapping small Python functions together creates powerful new ones — like building words from letters.
- Python Functional Pipelines See how chaining small Python functions into a pipeline turns messy data work into a clean assembly line.
- Python Monads In Python Understand monads through a simple lunchbox analogy — no math degree required, just curiosity.
- Ci Cd Why big apps can ship updates every day without turning your phone into a glitchy mess — CI/CD is the behind-the-scenes quality gate and delivery truck.
- Containerization Why does software that works on your computer break on everyone else's? Containers fix that — and they're why Netflix can deploy 100 updates a day without the site going down.