In Python, a curried function is a function that doesn’t take all its arguments at once. Instead, it takes them one by one—each time returning a new function that remembers the previous arguments. This continues until all the arguments are received and the final result can be produced.
Why would you want to do that? Because it lets you build your code in layers. You can create flexible, reusable functions that are easy to combine, customize, and share. Currying turns your functions into building blocks, perfect for writing clean and readable code.
Let’s look at a simple and fun example with names and speech:
def greet(name):
def say(thing):
return f"{name} says {thing}"
return say
speak = greet("Moody")
print(speak("hello!"))
Here, greet("Moody")
gives you back a new function that remembers the name Moody. When you call that returned function with "hello!"
, it prints “Moody says hello!”.
That’s currying in action. You’re creating a chain of functions, each handling a small piece of the task. It’s clean, reusable, and—most of all—fun. Let’s see how to create curried functions of your own.
Creating Curried Functions
The simplest way to create a curried function in Python is by using nested functions. That means writing a function inside another one. The outer function takes the first argument, and returns a new inner function that takes the second.
Let’s say you want to build a sandwich, but you want to pick the flavor first, then choose the food to go with it. You can split that into two steps using currying:
def add_flavor(flavor):
def add_food(food):
return f"{food} with {flavor}"
return add_food
sandwich = add_flavor("mustard")
print(sandwich("cheese")) # cheese with mustard
First, when you call add_flavor("mustard")
, it returns a new function that remembers the flavor “mustard.” This returned function is still waiting for the food. When you later call sandwich("cheese")
, you’re passing in the food, and it combines everything into the message "cheese with mustard"
.
This way, you can easily reuse the mustard flavor with different foods:
print(sandwich("ham")) # ham with mustard
print(sandwich("lettuce")) # lettuce with mustard
This pattern—splitting up the steps—makes your code more flexible and easy to reuse. It’s like preparing one part of a recipe ahead of time and then mixing in the final ingredient when you’re ready.
Currying with functools.partial
Python’s standard library includes a handy tool for currying: functools.partial
. It lets you take a function and “lock in” some of its arguments ahead of time. This creates a new function that only needs the remaining arguments.
Think of it like pre-filling part of a form so someone else only has to fill in the rest. You’re not changing the original function—you’re just creating a shortcut version of it.
Here’s a quick example using math:
from functools import partial
def multiply(x, y):
return x * y
double = partial(multiply, 2)
print(double(8)) # 16
The partial(multiply, 2)
creates a new function called double
that always multiplies something by 2. When you call double(8)
, it returns 16
. You can think of it as a curried function: the first argument is fixed, and the rest comes later.
This approach is especially useful when working with functions that get reused with the same starting values. It’s quick, clean, and built right into Python—no extra setup needed.
Real-World Uses of Currying
Currying isn’t just a coding trick—it’s something you can use in real programs to make your code cleaner and easier to manage. One common use is with filter functions. If you want to create reusable rules, currying lets you set up part of the logic and apply the rest later.
It’s also great for building string templates. You can create a function that prefixes or suffixes text and reuse it all over your code without repeating yourself.
Here’s a simple example using natural languages. Let’s say you’re building a translator and want to set the target language first, then provide the words later. Currying makes that easy:
def make_translator(lang):
def translate(word):
return f"{word} in {lang}"
return translate
french = make_translator("French")
print(french("apple"))
When you call make_translator("French")
, it returns a new function that remembers “French” as the target language. Then, calling french("apple")
prints “apple in French”.
You could just as easily make a Spanish or Swahili translator the same way. This makes your functions flexible, organized, and easy to extend—perfect for real-world projects that grow over time.
Currying with Lambdas
In Python, you can also write curried functions using lambda
. Lambdas are small, anonymous functions—perfect for quick and simple tasks. When you nest lambdas, you can create a curried function in just one line.
This is handy when you want something short and readable, without writing full def
blocks. Just like regular functions, each lambda
can take one argument and return another function.
Here’s a fun example using dog breeds:
bark = lambda breed: (
lambda times: f"{breed} barks " + "woof! " * times
)
beagle_bark = bark("Beagle")
print(beagle_bark(3))
This creates a function that first takes a dog breed, like "Beagle"
, and then returns another function that takes how many times the dog should bark.
It’s simple, quick, and perfect when you want to build a curried function on the fly. Just remember to keep lambdas readable—if things start to look messy, go back to regular def
functions.
Using Curried Functions with map()
and Friends
Curried functions work beautifully with Python’s higher-order functions like map()
, filter()
, and reduce()
. These tools take a function and a list (or other iterable), then apply the function to each item in the list.
When you combine currying with these tools, you can create reusable and flexible operations that fit right into your data processing.
Here’s a simple example where we add excitement to a list of things by appending an exclamation mark. First, we define a curried-style function:
def add_excitement(thing):
return f"{thing}!"
Now, given a list of favorite things:
things = ["Python", "Chess", "Music"]
You can use map()
to apply add_excitement
to each item:
print(list(map(add_excitement, things)))
This shows how simple functions—curried or not—can easily plug into powerful tools like map()
. You can combine currying with these to build flexible, reusable pipelines for your data and keep your code clean and fun.
Building Chains of Functions
Curried functions are great for building chains of small, reusable steps. Because each function returns another function, you can think of them like links in a chain, passing data along one piece at a time.
This style helps keep your code organized and easy to follow—especially when you want to combine multiple actions into a sequence.
Let’s try a fantasy-themed example with magic spells. Imagine you want to create different enchantments and cast them on targets one by one.
def enchant(spell):
def cast(target):
return f"{target} is now under the spell of {spell}!"
return cast
fireball = enchant("Fireball")
print(fireball("Orc"))
Here, calling enchant("Fireball")
returns a function that remembers the spell “Fireball.” Then, calling fireball("Orc")
applies that spell to the target "Orc"
, printing “Orc is now under the spell of Fireball!”.
With this pattern, you can easily create many spells and cast them on different creatures, chaining your magical functions smoothly and clearly. Currying helps your code tell a story, one step at a time.
Style Tips for Using Currying in Python
Currying can make your code cleaner and more flexible, but it’s best used where it actually improves clarity. Avoid overcomplicating things by currying every function—choose situations where breaking tasks into smaller steps makes your code easier to read and reuse.
Keep your curried functions short and focused on a single job. This helps others (and your future self) understand exactly what each part does without getting lost in details.
Also, give clear and meaningful names to the functions you return. Names like add_flavor
or make_greeter
help explain what the returned function is for, making your code more self-explanatory.
By following these simple style tips, you can write curried functions that are neat, useful, and fun to work with.
Closing Example: A Curried Zoo Adventure
To wrap everything up, here’s a small but complete example that combines all the ideas we’ve talked about. Imagine you’re running a zoo, and you want to create animals that can perform different actions.
This curried function lets you pick the animal first, then choose what it does next:
def make_animal(name):
def do(action):
return f"The {name} {action}s!"
return do
tiger = make_animal("tiger")
print(tiger("pounce"))
print(tiger("growl"))
First, make_animal("tiger")
creates a function that remembers the tiger’s name. Then, calling tiger("pounce")
or tiger("growl")
tells the tiger what to do.
This example shows how currying helps build flexible, readable code that can easily grow with your project—just like a real zoo full of lively animals!
Conclusion
Curried functions make your code modular, flexible, and even fun to write. They let you build reusable behaviors one step at a time, breaking down tasks into small, manageable pieces.
Whether you’re creating games, working with animals, or exploring languages, currying helps bring your code to life with clear and creative patterns.
Give curried functions a try in your own projects—you might find they open new doors to writing elegant and expressive Python.