Currying in Python: Transform Multi-Arg Functions
Table of Contents
Function currying is a specific kind of function transformation, where we translate a single function that accepts multiple arguments into multiple functions that each accept a single argument.
All the content from our Boot.dev courses are available for free here on the blog. This one is the "Currying" chapter of Learn Functional Programming in Python. If you want to try the far more immersive version of the course, do check it out!
How Do You Write a Curried Function in Python?
Here's a normal function that adds two numbers:
def add(a: int, b: int) -> int:
return a + b
print(add(1, 2))
# prints 3
And the same thing curried:
from collections.abc import Callable
def add(a: int) -> Callable[[int], int]:
def inner_add(b: int) -> int:
return a + b
return inner_add
print(add(1)(2))
# prints 3
The add function only takes a single input, a. It returns a new function that takes a single input, b. This new function, when called with a value for b, will return the sum of a and b.
Why Would You Curry a Function?
It's fairly obvious that a plain two-argument function is simpler than the nested, curried version. So why would we ever want to do the more complicated thing? Well, currying can be used to change a function's signature to make it conform to a specific shape. For example:
def colorize(converter: Callable[[str], str], doc: str) -> None:
# ...
converter(doc)
# ...
The colorize function accepts a function called converter as input, and at some point during its execution, it calls converter with a single argument. That means that it expects converter to accept exactly one argument. So, if I have a conversion function like this:
def markdown_to_html(doc: str, asterisk_style: str) -> str:
# ...
I can't pass markdown_to_html to colorize because markdown_to_html wants two arguments. To solve this problem, I can curry markdown_to_html into a function that takes a single argument:
def markdown_to_html(asterisk_style: str) -> Callable[[str], str]:
def asterisk_md_to_html(doc: str) -> str:
# do stuff with doc and asterisk_style...
return asterisk_md_to_html
markdown_to_html_italic: Callable[[str], str] = markdown_to_html("italic")
colorize(markdown_to_html_italic, doc)
How Do You Curry a Function With Three Arguments?
We can take a function that accepts multiple arguments:
final_volume: int = box_volume(3, 4, 5)
print(final_volume)
# 60
and convert it into a series of functions that each accept a single argument:
final_volume: int = box_volume(3)(4)(5)
print(final_volume)
# 60
box_volume(3)returns a new function that accepts a single integer and returns a new functionbox_volume(3)(4)returns another new function that accepts a single integer and returns a new functionbox_volume(3)(4)(5)returns the final result
Here's another way of calling it, where each function is stored in a variable before being called:
with_length_3 = box_volume(3)
with_len_3_width_4 = with_length_3(4)
final_volume: int = with_len_3_width_4(5)
print(final_volume)
# 60
Here are the function definitions:
from collections.abc import Callable
def box_volume(length: int) -> Callable[[int], Callable[[int], int]]:
def box_volume_with_len(width: int) -> Callable[[int], int]:
def box_volume_with_len_width(height: int) -> int:
return length * width * height
return box_volume_with_len_width
return box_volume_with_len
If functional patterns like this are new to you, the broader functional programming toolkit is worth a look, and if you're still nailing down the basics of Python functions, our Learn Python course covers them from the ground up.
Frequently Asked Questions
What is currying in Python?
Currying is transforming a function that takes multiple arguments into a chain of functions that each take a single argument. Instead of calling add(1, 2), a curried version is called add(1)(2), where each call returns the next function until the final result is produced.
Why would you curry a function?
Currying lets you change a function's signature so it conforms to a specific shape. If some code expects a function that takes one argument but yours takes two, you can curry yours into single-argument functions so it fits. It also lets you create reusable, partially applied functions.
What is the difference between currying and functools.partial?
functools.partial freezes some arguments of an existing multi-argument function and returns a new callable, without rewriting the original. Currying restructures the function itself into nested single-argument functions. Both reduce the number of arguments you pass at call time.
How do you curry a function with three arguments in Python?
Define a function that takes one argument and returns an inner function, which itself takes one argument and returns another inner function, and so on until the last function returns the final value. You then call it like box_volume(3)(4)(5).
