March 9, 2022

Tutorial: Lambda Functions in Python

In this tutorial, we will define lambda functions in Python and explore the advantages and limitations of employing them.

What is a Lambda Function in Python?

A lambda function is an anonymous function (i.e., defined without a name) that can take any number of arguments but, unlike normal functions, evaluates and returns only one expression.

A lambda function in Python has the following syntax:

lambda parameters: expression

The anatomy of a lambda function includes three elements:

  • The keyword lambda — an analog of def in normal functions
  • The parameters — support passing positional and keyword
    arguments, just like normal functions
  • The body — the expression for given parameters being evaluated
    with the lambda function

Note that, unlike a normal function, we don't surround the parameters of a lambda function with parentheses. If a lambda function takes two or more parameters, we list them with a comma.

We use a lambda function to evaluate only one short expression (ideally, a single-line) and only once, meaning that we aren't going to apply this function later. Usually, we pass a lambda function as an argument to a higher-order function (the one that takes in other functions as arguments), such as Python built-in functions like filter(), map(), or reduce().

How a Lambda Function in Python Works

Let's look at a simple example of a lambda function:

    lambda x: x + 1
    <function __main__.<lambda>(x)>

The lambda function above takes a single argument, increments it by 1, and returns the result. It's a simpler version of the following normal function with the def and return keywords:

    def increment_by_one(x):
        return x + 1

For now, however, our lambda function lambda x: x + 1 only creates a function object and doesn't return anything. We expected this: we didn't provide any value (an argument) to its parameter x. Let's assign a variable first, pass it to the lambda function, and see what we get this time:

    a = 2
    print(lambda x: a + 1)
    <function <lambda> at 0x00000250CB0A5820>

Instead of returning 3, as we might expect, our lambda function returned the function object itself and its memory location. Indeed, this isn't the right way to call a lambda function. To pass an argument to a lambda function, execute it, and return the result, we should use the following syntax:

    (lambda x: x + 1)(2)
    3

Note that while the parameter of our lambda function isn't surrounded with parentheses, when we call it, we add parentheses around the entire construction of the lambda function and around the argument we passed to it.

Another thing to notice in the code above is that with a lambda function, we can execute the function immediately after its creation and receive the result. This is the so-called immediately invoked function execution (or IIFE).

We can create a lambda function with multiple parameters. In this case, we separate the parameters in the function definition with a comma. When we execute such a lambda function, we list the corresponding arguments in the same order and separate them with a comma, too:

    (lambda x, y, z: x + y + z)(3, 8, 1)
    12

It's also possible to use a lambda function to perform conditional operations. Below is a lambda analog for a simple if-else function:

    print((lambda x: x if(x > 10) else 10)(5))
    print((lambda x: x if(x > 10) else 10)(12))
    10
    12

If multiple conditions are present (if-elif-...-else), we have to nest them:

    (lambda x: x * 10 if x > 10 else (x * 5 if x < 5 else x))(11)
    110

The issue with this approach is that already with one nested condition, the code becomes difficult to read, as we can see above. In such situations, a normal function with an if-elif-...-else set of conditions would be a better choice than a lambda function. Indeed, we can write the lambda function from the example above in the following way:

    def check_conditions(x):
        if x > 10:
            return x * 10
        elif x < 5:
            return x * 5
        else:
            return x

    check_conditions(11)
    110

Even though the function above spans more lines than the corresponding lambda function, it's much easier to read.

We can assign a lambda function to a variable and then call that variable as a normal function:

    increment = lambda x: x + 1
    increment(2)
    3

However, it's a bad practice, according the PEP 8 style guide for Python code:

The use of the assignment statement eliminates the sole benefit a lambda expression can offer over an explicit def statement (i.e., that it can be embedded inside a larger expression).

So, if we really need to store a function for further usage, instead of assigning a lambda function to a variable, we'd better define an equivalent normal function.

Applications of a Lambda Function in Python

Lambda with the filter() Function

We use the filter() function in Python to select certain items from
an iterable (like lists, sets, tuples, Series, etc.) based on predefined
criteria. It takes two arguments:

  • A function that defines the filtering criteria
  • An iterable on which the function runs

As a result of this operation, we get a filter object:

    lst = [33, 3, 22, 2, 11, 1]
    filter(lambda x: x > 10, lst)
    <filter at 0x250cb090520>

To get a new iterable from the filter object, with all the items from the original iterable that satisfy the predefined criteria, we need to pass the filter object to the corresponding function of the Python standard library: list(), tuple(), set(), frozenset(), or sorted() (to return a sorted list).

Let's filter a list of numbers selecting only the numbers greater than 10 and return a list sorted in ascending order:

    lst = [33, 3, 22, 2, 11, 1]
    sorted(filter(lambda x: x > 10, lst))
    [11, 22, 33]

We don't have to make a new iterable object of the same type as the original one. Also, we can store the result of this operation in a variable:

    lst = [33, 3, 22, 2, 11, 1]
    tpl = tuple(filter(lambda x: x > 10, lst))
    tpl
    (33, 22, 11)

Lambda with the map() Function

We use the map() function in Python to perform a certain operation on each item of an iterable. Its syntax is identical to filter(): a function to perform and an iterable to which this function applies. The map() function returns a map object, from which we can get a new iterable by passing this object to the corresponding Python function: list(), tuple(), set(), frozenset(), or sorted().

As with the filter() function, we can extract an iterable of a different type than the original one from a map object and also assign it to a variable. Below is an example of using the map() function to multiply each item in a list by 10 and output the mapped values as a tuple assigned to a variable tpl:

    lst = [1, 2, 3, 4, 5]
    print(map(lambda x: x * 10, lst))
    tpl = tuple(map(lambda x: x * 10, lst))
    tpl
    <map object at 0x00000250CB0D5F40>

    (10, 20, 30, 40, 50)

One important difference between the map() and filter() functions is that the first one always returns an iterable of the same lenth as the original one. So, since pandas Series objects are also iterables, we can apply the map() function on a DataFrame column to create a new column:

    import pandas as pd
    df = pd.DataFrame({'col1': [1, 2, 3, 4, 5], 'col2': [0, 0, 0, 0, 0]})
    print(df)
    df['col3'] = df['col1'].map(lambda x: x * 10)
    df
   col1  col2
0     1     0
1     2     0
2     3     0
3     4     0
4     5     0

   col1  col2  col3
0     1     0    10
1     2     0    20
2     3     0    30
3     4     0    40
4     5     0    50

Note that to get the same result in the above case, it's also possible to use the apply() function:

    df['col3'] = df['col1'].apply(lambda x: x * 10)
    df
   col1  col2  col3
0     1     0    10
1     2     0    20
2     3     0    30
3     4     0    40
4     5     0    50

We can also create a new DataFrame column based on some conditions for another column. Once again, for the code below, we can use the map() or apply() functions interchangeably:

    df['col4'] = df['col3'].map(lambda x: 30 if x < 30 else x)
    df
   col1  col2  col3  col4
0     1     0    10    30
1     2     0    20    30
2     3     0    30    30
3     4     0    40    40
4     5     0    50    50

Lambda with the reduce() Function

The reduce() function is related to the functools Python module, and it works in the following way:

  1. Operates on the first two items of an iterable and saves the result
  2. Operates on the saved result and the next item of the iterable
  3. Proceeds in this way over the pairs of values until all the items of
    the iterable are used

This function has the same two parameters as the previous two functions: a function and an iterable. However, unlike the previous functions, this one doesn't need to be passed to any other function and directly returns the resulting scalar value:

    from functools import reduce
    lst = [1, 2, 3, 4, 5]
    reduce(lambda x, y: x + y, lst)
    15

The code above shows the reduce() function in action when we use it to compute the sum of a list (even though for such a simple operation, we would use a better solution: sum(lst)).

Note that the reduce() function always requires a lambda function with exactly two parameters — and also that we have to import it first from the functools Python module.

Pros and Cons of a Lambda Function in Python

Pros

  • It's an ideal choice for evaluating a single expression that is
    supposed to be evaluated only once.
  • It can be invoked as soon as it is defined.
  • Its syntax is more compact in comparison to a corresponding normal
    function.
  • It can be passed as a parameter to a higher-order function, like
    filter(), map(), and reduce().

Cons

  • It can't perform multiple expressions.
  • It can easily become cumbersome, for example when it
    includes an if-elif-...-else cycle.
  • It can't contain any variable assignements (e.g., lambda x: x=0
    will throw a SyntaxError).
  • We can't provide a docstring to a lambda function.

Conclusion

To summarize, we have discussed in detail many aspects of defining and using lambda functions in Python:

  • How a lambda function differs from a normal Python function
  • The syntax and anatomy of a lambda function in Python
  • When to use a lambda function
  • How a lambda function works
  • How to call a lambda function
  • The definition of invoked function execution (IIFE)
  • How to perform conditional operations with a lambda function, how to
    nest multiple conditions, and why we should avoid it
  • Why we should avoid assigning a lambda function to a variable
  • How to use a lambda function with the filter() function
  • How to use a lambda function with the map() function
  • How we can create a new column in a pandas DataFrame using the
    map() function with a lambda function passed to it — and an
    alternative function to use in such cases
  • How to use a lambda function with the reduce() function
  • The pros and cons of using a lambda function over a normal Python
    function

Hopefully, this tutorial has helped make the seemingly intimidating concept of a lambda function in Python clearer and easier to apply!

Elena Kosourova

About the author

Elena Kosourova

Elena is a petroleum geologist and community manager at Dataquest. You can find her chatting online with data enthusiasts and writing tutorials on data science topics. Find her on LinkedIn.