A Guide to Python Lambda Functions2020-08-10
In Python, Lambda functions are rare compared to “normal” functions, and occasionally misunderstood or overused.
In this article we’ll cover what Lambda functions are, how to use them, and how they compare with normal functions and other alternatives. We’ll also touch on the history of lambda functions, such as where the name “lambda” came from and why Python’s creator Guido van Rossum wanted to remove them.
- What Lambda Functions Are (in Python)
- Examples of Using Lambda Functions in Python
- Alternatives to Lambda Functions
What Lambda Functions Are (in Python)
lambda keyword allows you to define a lambda function.
This is a function that returns a single expression in one line.
It’s like a shortcut for using
def to create a normal function, which requires at least two lines.
For example, we could define a simple addition function using
def like so:
This takes two lines.
lambda we can instead write our function as:
This takes only one line.
To convert the syntax, we:
- Removed the parentheses around the arguments.
- Put the function body on the same line as its definition, after the single colon (
- Removed the
returnkeyword. Lambda functions always return the result of their one expression.
- Added an explicit assignment of the function to the name
Despite all these syntactical differences, the two versions work identically:
Why They’re Called Lambda Functions
Lambda, or λ, is the 11th letter of the Greek alphabet. Due to the use of the Greek alphabet in mathematics, Alonzo Church ended up using it in the 1930’s when describing a concept he called Lambda calculus. This is a formal system describing any possible computation - something like a purely mathematical programming language.
Lambda calculus is so-called because it uses Lambda (λ) to represent functions, which also never have names. The Lisp programming language copied this concept, and Python copied it from Lisp.
Examples of Using Lambda Functions in Python
The main motivation for using
lambda is to create a function and use it as an argument in the same line.
These are often done with several built-in functions that take functions as arguments.
Let’s look at three examples now.
For our examples, let’s use a list of puppies with their cuteness ratings:
We’ll manipulate the
puppies list with some Python built-ins, to which we will pass lambda functions.
list.sort() method takes with an optional
This is a function to map the list items to values to sort them by.
We can use it to sort our puppies by their increasing cuteness ratings, by passing
key as a function that extracts a given puppy’s cuteness value.
def, we need to define the function separately before we call
lambda, we can define the function inside the call to
lambda version is only one line, whilst the
def version is three lines (four if you count the blank line between the function and call to
We can make it even shorter by using a one letter variable name inside the lambda function:
sorted() built-in similarly takes a
key argument, but it takes with any iterable instead of just lists, so you’ll often see
lambda used in conjunction with it.
filter() built-in takes a function and an iterable, and returns the items from the iterable for which the function returned true.
We can use it to filter our puppies to only the cutest ones, by passing
key as a function that returns if a given puppy has enough cuteness.
def, we again need to define the function separately, before we call
(Note we need to call
filter() to see its results, because it is a generator.)
lambda, we can again define the function in the same line as its use:
Again, we’ve saved a few lines.
map() built-in takes a function and an iterable, and returns a new iterable of the results of applying the function on the items in the passed iterable.
We can use it to extract our puppies’ names into a list of strings.
def, we again need to define the function separately, before we call
(Note we again need to call
map() to see its results, because it is also a generator.)
lambda, we can once again define the function in the same line as its use, saving some lines:
Alternatives to Lambda Functions
The existence of lambda functions in Python is somewhat controversial.
The creator of Python, Guido van Rossum, even advertised his intention to remove it in Python 3.0, along with
In his 2005 post The fate of reduce() in Python 3000, he wrote:
About 12 years ago, Python aquired lambda, reduce(), filter() and map()… But, despite of the PR value, I think these features should be cut from Python 3000.
(Python 3000 was the working name for Python 3.0.)
Ultimately Python kept
lambda for backwards compatibility, and Guido updated the post with:
lambda, filter and map will stay (the latter two with small changes, returning iterators instead of lists). Only reduce will be removed from the 3.0 standard library. You can import it from functools.
But there are still alternatives to using a
lambda function, and they are preferable for many use cases.
Let’s look at those now.
The first alternative is to use a normal function.
We already compared these with their corresponding lambda functions in our three examples above.
Normal functions have a number of advantages that the
lambda syntax does not allow.
a) Normal Function Advantage 1 - Naming
Normal functions have a name, which allows us to clarify our intention. With a complex lambda function you might find yourself writing a comment to describe what it does. Using a normal function you can embed this informatino in the function’s name itself.
filter() example again.
Imagine the filtering we did was because there’s a minimum of cuteness of 100 to enter a contest.
We might try embed this in the lambda function version with a comment, which requires us to split
filter() across multiple lines:
But with a normal function, we can put that information in the function name:
Note we can give lambda functions names too by assigning them:
But if you check this function’s
__name__ attribute, you’ll see it’s actually called
All lambda functions have the name
'<lambda>', even after we assign them to variables.
This is because Python doesn’t have any name information when creating the function.
This will appears in various code inspection tools, including stack traces, and can make debugging a little harder.
Normal Function Advantage 2 - Expression Splitting
Our previous examples all used short functions, so the
lambda syntax was readable on a single line.
But if our function contained a longer expression, using a
lambda function could mean cramming lots of code on one line.
Imagine we wanted to sort our puppies in a more complex way: in reverse order, by the upper-cased first letter of the last part of their names.
lambda, our call to
list.sort() would look like this:
This line contains a lot of different pieces. I count 14 different object names, argument names, keywords, and values, plus a lot of punctuation. That’s a lot to read and understand at once!
We could improve the readability a bit by splitting the code over multiple lines:
But then we have given up some of the benefit of using
lambda, as we have the same number of lines of code as if we hadn’t used it.
The lambda function is also still quite a lot of steps to understand.
By using a normal function, we can split the expression in two pieces, and assign a name to the intermediate
Now we can much more easily follow the calculation, and we’ve again used the function name to clarify our intention.
Normal Function Advantage 3 - Clearer Decorators
Python’s decorator syntax allows us to extend the behavior of a function with a wrapper.
When declaring a function with
lambda, it’s still possible to use decorators, but without the
@ syntax which highlights use as a decorator.
For example, if we found that our “is cute enough” check was taking a significant amount of time, we could add caching with the
functools.lru_cache decorator from the standard library.
Using a normal function, we can add it with the
lambda we have to call the decorator ourselves, without
This works, but it slightly obscures
lru_cache being a decorator.
Normal Function Advantage 4 - Function Annotations
Python’s function annotations allow us to add for type hints. Such hints declare the expected types of variables and we can verify our expectations with a type checker tool such as mypy. These let us make extra guarantees of our code’s correctness, alongside tests.
Unfortunately, because function annotations use colons (
:) as their separator, they are not compatible with
lambda already uses a single colon to separate its arguments from its expression, so there’s nowhere to add annotations.
For example, we could annotate our previous
is_cute_enough_for_contest() function like so:
This declares that we expect it to take a
Puppy object and return a
We could run mypy to check that
is_cute_enough_for_contest() is always called with such types.
If we try to add such annotations to a
lambda, we’ll only get a
This is because the first colon starts the function body.
Normal Function Advantage 5 - Accurate Test Coverage
One tool for ensuring your tests check all parts of your system is to measure test coverage with coverage.py. This works on a line-by-line basis.
Because lambda functions include their body on the same line as their definition, they will show as fully covered, even if the function is never called. Thus, you might miss bugs in your lambda functions’ bodies, such as mistyping an attribute name.
Normal functions aren’t subject to this problem, because their body starts on a separate line to their declaration. If they don’t get called during tests, coverage will always show them as uncovered.
List Comprehensions (And Other Types)
The second alternative to many of the uses of
lambda is to use a comprehension.
Many of the built-in functions that lambda functions are typically used with use the passed function to generate a new list of items, so a list comprehension is appropriate.
But your use case might mean using a set or dict comprehension, or a generator expression.
For example, any call to
filter() can be rewritten with an equivalent list comprehension.
Take our cuteness
We can rewrite it using a list comprehension as:
We can also put the condition inside the comprehension:
Using a comprehension without a function call like this is even a little bit faster. This is because the expression uses local variable access, rather passing them into another function with its own namespace.
map() call can be rewritten as a list comprehension.
Again, take our previous
We can rewrite this as:
This is again simpler.
Comprehensions offer quite flexible syntax, allowing the same things that a
for loop would, and so they’re more generally useful than
A third alternative to writing lambda functions is to use the standard library’s
This module contains some predefined functions and function factories, which can replace the most common use cases for lambda functions.
Let’s look at both of these separtaely, factories first.
The function factories offered by
operator create functions that return a value based on their input.
These can replace a lot of common use cases for lambda functions.
Recall the lambda function we used with
We can construct an identical function with the
operator.attrgetter function factory.
We pass it the name the attribute we want to extract, and it returns a function that does so:
We can use this inline in our
The “sort by an attribute” pattern is quite common, so
attrgetter is often used to implement it.
Another note: the function that
attrgetter returns is implemented in C, so it’s slightly faster than using either a normal or lambda function.
operator module also offers two other function factories.
itemgetter can replace a lambda that gets an item with
, such as
lambda puppies: puppies.
methodcaller can replace a lambda that calls a method, such as
lambda puppy: puppy.bark().
Another set of functions offered by the
operator module are its wrappers of Python’s operators (hence the module’s name).
These can replace another bunch of common use cases for lambda functions.
For example, imagine we had a mega-cuteness formula that required us to multiply our puppies’ extracted cuteness values together.
We could use
functools.reduce() function with a lambda function to do this.
reduce() will take pairs of puppies
Our lambda function,
lambda a, b: a * b, is equivalent to
operator.mul (short for multiply).
So we could also write:
Again because the
operator function is implemented in C, it is slightly faster than our handwritten version.
The operator module provides wrapper functions for all of Python’s operators, so there’s no need to write such lambda functions.
I hope this guide to lambda functions has answered many of your questions about them. May your Python be ever more readable,
Working on a Django project? Check out my book Speed Up Your Django Tests which covers loads of best practices so you can write faster, more accurate tests.
One summary email a week, no spam, I pinky promise.
- Better Python Decorators with wrapt
- How to Combine Two Python Decorators
- Avoid Hardcoding ID’s in Your Tests
© 2020 All rights reserved.