Python Lambda Functions

Chapter 34 11 mins

Learning outcomes:

  1. What are lambda functions
  2. The lambda keyword in Python
  3. More details on lambda
  4. Practical-level usage of lambda functions

What are lambda functions?

In lambda calculus, lambda functions are functions without a name. From mathematics, the same idea has found its way into computer programming languages.

So, defining it formally

A lambda function is a function without any name.

Other common names used to refer to lambda functions are lambda expressions, lambda abstractions, function literals and anonymous functions.

All these different terms make perfect sense:

  1. Lambda functions are expressions unlike normal functions in programming languages (defined using def in the case of Python) which are statements. Hence, they are also known as lambda expressions.
  2. Lambda functions are abstractions over the long way of defining named functions in given languages. In Python, as we shall see below the abstraction brought about by lambda functions over normal function definition is fairly considerable. Hence, they are also known as lambda abstractions.
  3. Just like 10 denotes an integer directly, as is, a lambda function denotes a function directly. That is, a lambda function is a literal for the function data type. Hence, lambda functions are also known as function literals.
  4. A lambda function, as stated before, doesn't have a name of its own. In other words, it's an anonymous function. Hence lambda functions are also known as anonymous functions.

All functional programming languages, including Python, provide support for lambda functions, in some way.

These functions are meant to define simple and short functions, mostly one-liner, that perform a computation and return its result.

Let's explore lambdas provided by the Python programming language.

Lambda functions in Python

In Python, a lambda function can be created via a similar sounding keyword — lambda.

Shown below is the syntax of lambda when used to define a function without any parameters:

lambda: expression

First comes the keyword and then a valid Python expression. This expression is evaluated and returned when the lambda function is invoked.

Let's consider a quick example:

x = lambda: 100

Here we create a lambda function that returns 100 when called, and then save it in the variable x. Next we invoke x.

And here's what we get

x()
100

The value 100 printed to the shell. Not even a pinch difficult, is it?

Moving on, it's also possible to define parameters in a lambda function.

Shown below is the syntax to do so:

lambda parameters: expression

As before, it begins with the lambda keyword. Then comes a list of parameters just as they would be specified inside the pair of parentheses in a normal function definition.

Lastly, comes the expression after the : colon. All parameters of the lambda function are accessible in expression — otherwise, what would be the point of even having them!

Below we create a couple of lambda functions having parameters.

First, let's consider a simple doubler function — you give it a number as argument and it returns back its double:

double = lambda x: x * 2
double(2)
4
double(100)
20
double(0.5)
1.0

Next, let's consider a simple summing function: you give it two nums and it returns back their sum.

sum = lambda a, b: a + b
sum(10, 20)
30
sum(5, 2.5)
7.5
sum(120, -5)
115

Why not create a function to retrieve the first element of any given sequence?

Let's put up a more complex lambda function computing the distance between two co-ordinates on a Cartesian plane.

Each co-ordinate ought to be provided as a tuple (or at least an iterable) of two elements, whose first item is the x value while the second value is the y value.

distance = lambda a, b: ((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2) ** 0.5

Below, we compute the distances between a couple of Cartesian co-ordinates:

distance((0, 5), (1, 1))
4.123105625617661
distance((0, 0), (3, 4))
5.0

More on lambda functions

As we saw above, constructing lambda functions in Python is very easy. Just write the lambda keyword, an optional list of parameters, and then an expression — that's it. You have a functional function in hand!

However, there are some hidden details regarding lambdas in Python that we must know in order to keep from using them in incorrect ways.

They are discussed as follows:

Multiple statements can't be given

One might think that it's possible to put multiple statements inside a lambda function by separating them using a ; semi-colon.

However, if we do so, what would happen is something else.

Consider the following code and try to think what would happen here:

f = lambda x: x + 5; print('Hello')

This code is perfectly valid — it doesn't have any syntax errors. But it surely has a semantic error. That is, the code doesn't work out as it's expected to.

As soon as the code gets executed, we get 'Hello' printed regardless of whether we call f() or not. In fact, we get no output, when we call f(), which is what we actually desire.

So why does this happen?

The semi-colon in the code above separates the two statements f = lambda x: x + 5 and print('Hello'), contrary to what some might think that it separates the two expressions x + 5 and print('Hello').

The code is similar to the following:

f = lambda x: x + 5
print('Hello')

So, in short, it's not possible to have multiple statements inside a lambda function.

co_name on lambdas returns '<lambda>'

Although a lambda function is an anonymous function i.e has no name, the co_name attribute of its code object holds the string '<lambda>'. This is to indicate that the underlying function is a lambda function.

The following snippet demonstrates this:

f = lambda x: x
print(f.__code__.co_name)
<lambda>
The word '<lambda>' can't be the name of a normal function in Python, since it contains two invalid characters: < and >.

Practical usage of lambda

In the section above, we considered a handful of lambda functions each of which accomplished a particular task. For instance, the one in sum added two numbers, the one in distance calculated the distance between two points on a Cartesian plane and so on.

However, we'd be better off at defining such functions using the normal function definition syntax in Python. But why?

That's because all these functions do something that could be better understood by a name. Moreover, these functions aren't meant for a one-time use, rather they are meant to be used again and again.

For instance, when we create a lambda function to compute the sum of two numbers, at the end of the day, we save it in a variable named sum. In other words, we aim to describe the function's purpose using a name.

Anonymous functions are NOT meant for such scenarios.

What's the point of leaving out the name of the function (in the lambda expression), when you have to name it, indirectly, later on (by assigning it to a named identifier)?

Instead,

Lambda expressions are meant for places where simple and short functions are desired, naming which would be unnecessary and just overcomplicate things.

Let's consider an example to help clarify this notion.

Suppose we want to sort the following two lists in ascending order: the first one based on the length of each string, while the second one based on the second number of each tuple:

a = ['Hello', 'Bye', 'My', 'Lovely', 'Incorporation', 'Hi']
b = [(0, 5), (2, -1), (10, 5), (2, -5), (0, 0), (-1, 1)]

As we know back from the Python List Sorting chapter, this could be achieved using the sort() list method, along with its key parameter.

For the first list, we need to provide, as key, a function that returns the length of a given list element. For the second list, we need to provide a function that returns the second item of a given list element.

Now, if we go with the normal way to define these functions, we'd get something like this:

a = ['Hello', 'Bye', 'My', 'Lovely', 'Incorporation', 'Hi']
b = [(0, 5), (2, -1), (10, 5), (2, -5), (0, 0), (-1, 1)]

def get_length(item):
   return len(item)

def get_second_item(item):
   return item[1]

a.sort(key=get_length)
b.sort(key=get_second_item)

print(a)
print(b)
['My', 'Hi', 'Bye', 'Hello', 'Lovely', 'Incorporation'] [(2, -5), (2, -1), (0, 0), (-1, 1), (0, 5), (10, 5)]

See how we have an unnecessary name for each of the given functions get_length() and get_second_item().

The names are clearly redundant — they overcrowd the global namespace for no reason whatsoever. We have to use both the functions only once, while we are sorting the given lists, after which there is absolutely no need of the functions.

This is what lambda functions were made for, i.e to create functions that don't need to be named.

Let's rewrite the code above using lambda functions instead of get_length() and get_second_item():

a = ['Hello', 'Bye', 'My', 'Lovely', 'Incorporation', 'Hi']
b = [(0, 5), (2, -1), (10, 5), (2, -5), (0, 0), (-1, 1)]

a.sort(key=lambda x: len(x))
b.sort(key=lambda x: x[1])

print(a)
print(b)
['My', 'Hi', 'Bye', 'Hello', 'Lovely', 'Incorporation'] [(2, -5), (2, -1), (0, 0), (-1, 1), (0, 5), (10, 5)]

First of all, it's clearly apparent that this code is much simpler and shorter than the previous one. Secondly, we aren't crowding the global namespace with unnecessary function names at all.

When calling sort(), we need to right away provide a function to it as key. Mostly, keys are simple, one-liner functions, and this goes with the whole essence of lambdas.

To boil it all down, lambda functions are designed for cases where specifically naming a simple function is not really required, and would otherwise be extra work.