Hướng dẫn dùng define removing python

Python and other languages like Java, C#, and even C++ have had lambda functions added to their syntax, whereas languages like LISP or the ML family of languages, Haskell, OCaml, and F#, use lambdas as a core concept.

Python lambdas are little, anonymous functions, subject to a more restrictive but more concise syntax than regular Python functions.

By the end of this article, you’ll know:

  • How Python lambdas came to be
  • How lambdas compare with regular function objects
  • How to write lambda functions
  • Which functions in the Python standard library leverage lambdas
  • When to use or avoid Python lambda functions

Notes: You’ll see some code examples using

def add_one[x]:
    return x + 1
2 that seem to blatantly ignore Python style best practices. This is only intended to illustrate lambda calculus concepts or to highlight the capabilities of Python
def add_one[x]:
    return x + 1
2.

Those questionable examples will be contrasted with better approaches or alternatives as you progress through the article.

This tutorial is mainly for intermediate to experienced Python programmers, but it is accessible to any curious minds with interest in programming and lambda calculus.

All the examples included in this tutorial have been tested with Python 3.7.

Take the Quiz: Test your knowledge with our interactive “Python Lambda Functions” quiz. Upon completion you will receive a score so you can track your learning progress over time:

Take the Quiz »

Free Download: Get a sample chapter from Python Tricks: The Book that shows you Python’s best practices with simple examples you can apply instantly to write more beautiful + Pythonic code.

Lambda Calculus

Lambda expressions in Python and other programming languages have their roots in lambda calculus, a model of computation invented by Alonzo Church. You’ll uncover when lambda calculus was introduced and why it’s a fundamental concept that ended up in the Python ecosystem.

Remove ads

History

Alonzo Church formalized lambda calculus, a language based on pure abstraction, in the 1930s. Lambda functions are also referred to as lambda abstractions, a direct reference to the abstraction model of Alonzo Church’s original creation.

Lambda calculus can encode any computation. It is Turing complete, but contrary to the concept of a Turing machine, it is pure and does not keep any state.

Functional languages get their origin in mathematical logic and lambda calculus, while imperative programming languages embrace the state-based model of computation invented by Alan Turing. The two models of computation, lambda calculus and Turing machines, can be translated into each another. This equivalence is known as the Church-Turing hypothesis.

Functional languages directly inherit the lambda calculus philosophy, adopting a declarative approach of programming that emphasizes abstraction, data transformation, composition, and purity [no state and no side effects]. Examples of functional languages include Haskell, Lisp, or Erlang.

By contrast, the Turing Machine led to imperative programming found in languages like Fortran, C, or Python.

The imperative style consists of programming with statements, driving the flow of the program step by step with detailed instructions. This approach promotes mutation and requires managing state.

The separation in both families presents some nuances, as some functional languages incorporate imperative features, like OCaml, while functional features have been permeating the imperative family of languages in particular with the introduction of lambda functions in Java, or Python.

Python is not inherently a functional language, but it adopted some functional concepts early on. In January 1994,

def add_one[x]:
    return x + 1
4,
def add_one[x]:
    return x + 1
5,
def add_one[x]:
    return x + 1
6, and the
def add_one[x]:
    return x + 1
2 operator were added to the language.

First Example

Here are a few examples to give you an appetite for some Python code, functional style.

The identity function, a function that returns its argument, is expressed with a standard Python function definition using the keyword

def add_one[x]:
    return x + 1
8 as follows:

>>>

>>> def identity[x]:
...     return x

def add_one[x]:
    return x + 1
9 takes an argument
>>> full_name = lambda first, last: f'Full name: {first.title[]} {last.title[]}'
>>> full_name['guido', 'van rossum']
'Full name: Guido Van Rossum'
0 and returns it upon invocation.

In contrast, if you use a Python lambda construction, you get the following:

>>>

>>> lambda x: x

In the example above, the expression is composed of:

  • The keyword:
    def add_one[x]:
        return x + 1
    
    2
  • A bound variable:
    >>> full_name = lambda first, last: f'Full name: {first.title[]} {last.title[]}'
    >>> full_name['guido', 'van rossum']
    'Full name: Guido Van Rossum'
    
    0
  • A body:
    >>> full_name = lambda first, last: f'Full name: {first.title[]} {last.title[]}'
    >>> full_name['guido', 'van rossum']
    'Full name: Guido Van Rossum'
    
    0

Note: In the context of this article, a bound variable is an argument to a lambda function.

In contrast, a free variable is not bound and may be referenced in the body of the expression. A free variable can be a constant or a variable defined in the enclosing scope of the function.

You can write a slightly more elaborated example, a function that adds

>>> full_name = lambda first, last: f'Full name: {first.title[]} {last.title[]}'
>>> full_name['guido', 'van rossum']
'Full name: Guido Van Rossum'
4 to an argument, as follows:

>>>

>>> lambda x: x + 1

You can apply the function above to an argument by surrounding the function and its argument with parentheses:

>>>

>>> [lambda x: x + 1][2]
3

Reduction is a lambda calculus strategy to compute the value of the expression. In the current example, it consists of replacing the bound variable

>>> full_name = lambda first, last: f'Full name: {first.title[]} {last.title[]}'
>>> full_name['guido', 'van rossum']
'Full name: Guido Van Rossum'
0 with the argument
>>> full_name = lambda first, last: f'Full name: {first.title[]} {last.title[]}'
>>> full_name['guido', 'van rossum']
'Full name: Guido Van Rossum'
6:

[lambda x: x + 1][2] = lambda 2: 2 + 1
                     = 2 + 1
                     = 3

Because a lambda function is an expression, it can be named. Therefore you could write the previous code as follows:

>>>

>>> add_one = lambda x: x + 1
>>> add_one[2]
3

The above lambda function is equivalent to writing this:

def add_one[x]:
    return x + 1

These functions all take a single argument. You may have noticed that, in the definition of the lambdas, the arguments don’t have parentheses around them. Multi-argument functions [functions that take more than one argument] are expressed in Python lambdas by listing arguments and separating them with a comma [

>>> full_name = lambda first, last: f'Full name: {first.title[]} {last.title[]}'
>>> full_name['guido', 'van rossum']
'Full name: Guido Van Rossum'
7] but without surrounding them with parentheses:

>>>

>>> full_name = lambda first, last: f'Full name: {first.title[]} {last.title[]}'
>>> full_name['guido', 'van rossum']
'Full name: Guido Van Rossum'

The lambda function assigned to

>>> full_name = lambda first, last: f'Full name: {first.title[]} {last.title[]}'
>>> full_name['guido', 'van rossum']
'Full name: Guido Van Rossum'
8 takes two arguments and returns a string interpolating the two parameters
>>> full_name = lambda first, last: f'Full name: {first.title[]} {last.title[]}'
>>> full_name['guido', 'van rossum']
'Full name: Guido Van Rossum'
9 and
>>> lambda x, y: x + y
0. As expected, the definition of the lambda lists the arguments with no parentheses, whereas calling the function is done exactly like a normal Python function, with parentheses surrounding the arguments.

Remove ads

Anonymous Functions

The following terms may be used interchangeably depending on the programming language type and culture:

  • Anonymous functions
  • Lambda functions
  • Lambda expressions
  • Lambda abstractions
  • Lambda form
  • Function literals

For the rest of this article after this section, you’ll mostly see the term lambda function.

Taken literally, an anonymous function is a function without a name. In Python, an anonymous function is created with the

def add_one[x]:
    return x + 1
2 keyword. More loosely, it may or not be assigned a name. Consider a two-argument anonymous function defined with
def add_one[x]:
    return x + 1
2 but not bound to a variable. The lambda is not given a name:

>>>

>>> lambda x, y: x + y

The function above defines a lambda expression that takes two arguments and returns their sum.

Other than providing you with the feedback that Python is perfectly fine with this form, it doesn’t lead to any practical use. You could invoke the function in the Python interpreter:

>>>

>>> _[1, 2]
3

The example above is taking advantage of the interactive interpreter-only feature provided via the underscore [

>>> lambda x, y: x + y
3]. See the note below for more details.

You could not write similar code in a Python module. Consider the

>>> lambda x, y: x + y
3 in the interpreter as a side effect that you took advantage of. In a Python module, you would assign a name to the lambda, or you would pass the lambda to a function. You’ll use those two approaches later in this article.

Note: In the interactive interpreter, the single underscore [

>>> lambda x, y: x + y
3] is bound to the last expression evaluated.

In the example above, the

>>> lambda x, y: x + y
3 points to the lambda function. For more details about the usage of this special character in Python, check out The Meaning of Underscores in Python.

Another pattern used in other languages like JavaScript is to immediately execute a Python lambda function. This is known as an Immediately Invoked Function Expression [IIFE, pronounce “iffy”]. Here’s an example:

>>>

>>> lambda x: x
0

The lambda function above is defined and then immediately called with two arguments [

>>> full_name = lambda first, last: f'Full name: {first.title[]} {last.title[]}'
>>> full_name['guido', 'van rossum']
'Full name: Guido Van Rossum'
6 and
>>> lambda x, y: x + y
8]. It returns the value
>>> lambda x, y: x + y
9, which is the sum of the arguments.

Several examples in this tutorial use this format to highlight the anonymous aspect of a lambda function and avoid focusing on

def add_one[x]:
    return x + 1
2 in Python as a shorter way of defining a function.

Python does not encourage using immediately invoked lambda expressions. It simply results from a lambda expression being callable, unlike the body of a normal function.

Lambda functions are frequently used with higher-order functions, which take one or more functions as arguments or return one or more functions.

A lambda function can be a higher-order function by taking a function [normal or lambda] as an argument like in the following contrived example:

>>>

>>> lambda x: x
1

Python exposes higher-order functions as built-in functions or in the standard library. Examples include

def add_one[x]:
    return x + 1
4,
def add_one[x]:
    return x + 1
5,
>>> _[1, 2]
3
3, as well as key functions like
>>> _[1, 2]
3
4,
>>> _[1, 2]
3
5,
>>> _[1, 2]
3
6, and
>>> _[1, 2]
3
7. You’ll use lambda functions together with Python higher-order functions in Appropriate Uses of Lambda Expressions.

Remove ads

Python Lambda and Regular Functions

This quote from the Python Design and History FAQ seems to set the tone about the overall expectation regarding the usage of lambda functions in Python:

Unlike lambda forms in other languages, where they add functionality, Python lambdas are only a shorthand notation if you’re too lazy to define a function. [Source]

Nevertheless, don’t let this statement deter you from using Python’s

def add_one[x]:
    return x + 1
2. At first glance, you may accept that a lambda function is a function with some syntactic sugar shortening the code to define or invoke a function. The following sections highlight the commonalities and subtle differences between normal Python functions and lambda functions.

Functions

At this point, you may wonder what fundamentally distinguishes a lambda function bound to a variable from a regular function with a single

>>> _[1, 2]
3
9 line: under the surface, almost nothing. Let’s verify how Python sees a function built with a single return statement versus a function constructed as an expression [
def add_one[x]:
    return x + 1
2].

The

>>> lambda x: x
01 module exposes functions to analyze Python bytecode generated by the Python compiler:

>>>

>>> lambda x: x
2

You can see that

>>> lambda x: x
02 expose a readable version of the Python bytecode allowing the inspection of the low-level instructions that the Python interpreter will use while executing the program.

Now see it with a regular function object:

>>>

>>> lambda x: x
3

The bytecode interpreted by Python is the same for both functions. But you may notice that the naming is different: the function name is

>>> lambda x: x
03 for a function defined with
def add_one[x]:
    return x + 1
8, whereas the Python lambda function is seen as
def add_one[x]:
    return x + 1
2.

Traceback

You saw in the previous section that, in the context of the lambda function, Python did not provide the name of the function, but only

>>> lambda x: x
06. This can be a limitation to consider when an exception occurs, and a traceback shows only
>>> lambda x: x
06:

>>>

>>> lambda x: x
4

The traceback of an exception raised while a lambda function is executed only identifies the function causing the exception as

>>> lambda x: x
06.

Here’s the same exception raised by a normal function:

>>>

>>> lambda x: x
5

The normal function causes a similar error but results in a more precise traceback because it gives the function name,

>>> lambda x: x
09.

Syntax

As you saw in the previous sections, a lambda form presents syntactic distinctions from a normal function. In particular, a lambda function has the following characteristics:

  • It can only contain expressions and can’t include statements in its body.
  • It is written as a single line of execution.
  • It does not support type annotations.
  • It can be immediately invoked [IIFE].

No Statements

A lambda function can’t contain any statements. In a lambda function, statements like

>>> _[1, 2]
3
9,
>>> lambda x: x
11,
>>> lambda x: x
12, or
>>> lambda x: x
13 will raise a
>>> lambda x: x
14 exception. Here’s an example of adding
>>> lambda x: x
12 to the body of a lambda:

>>>

>>> lambda x: x
6

This contrived example intended to

>>> lambda x: x
12 that parameter
>>> full_name = lambda first, last: f'Full name: {first.title[]} {last.title[]}'
>>> full_name['guido', 'van rossum']
'Full name: Guido Van Rossum'
0 had a value of
>>> full_name = lambda first, last: f'Full name: {first.title[]} {last.title[]}'
>>> full_name['guido', 'van rossum']
'Full name: Guido Van Rossum'
6. But, the interpreter identifies a
>>> lambda x: x
14 while parsing the code that involves the statement
>>> lambda x: x
12 in the body of the
def add_one[x]:
    return x + 1
2.

Single Expression

In contrast to a normal function, a Python lambda function is a single expression. Although, in the body of a

def add_one[x]:
    return x + 1
2, you can spread the expression over several lines using parentheses or a multiline string, it remains a single expression:

>>>

>>> lambda x: x
7

The example above returns the string

>>> lambda x: x
23 when the lambda argument is odd, and
>>> lambda x: x
24 when the argument is even. It spreads across two lines because it is contained in a set of parentheses, but it remains a single expression.

Type Annotations

If you’ve started adopting type hinting, which is now available in Python, then you have another good reason to prefer normal functions over Python lambda functions. Check out Python Type Checking [Guide] to get learn more about Python type hints and type checking. In a lambda function, there is no equivalent for the following:

>>> lambda x: x
8

Any type error with

>>> lambda x: x
25 can be caught by tools like
>>> lambda x: x
26 or
>>> lambda x: x
27, whereas a
>>> lambda x: x
14 with the equivalent lambda function is raised at runtime:

>>>

>>> lambda x: x
9

Like trying to include a statement in a lambda, adding type annotation immediately results in a

>>> lambda x: x
14 at runtime.

IIFE

You’ve already seen several examples of immediately invoked function execution:

>>>

>>> lambda x: x + 1
0

Outside of the Python interpreter, this feature is probably not used in practice. It’s a direct consequence of a lambda function being callable as it is defined. For example, this allows you to pass the definition of a Python lambda expression to a higher-order function like

def add_one[x]:
    return x + 1
4,
def add_one[x]:
    return x + 1
5, or
>>> _[1, 2]
3
3, or to a key function.

Remove ads

Arguments

Like a normal function object defined with

def add_one[x]:
    return x + 1
8, Python lambda expressions support all the different ways of passing arguments. This includes:

  • Positional arguments
  • Named arguments [sometimes called keyword arguments]
  • Variable list of arguments [often referred to as varargs]
  • Variable list of keyword arguments
  • Keyword-only arguments

The following examples illustrate options open to you in order to pass arguments to lambda expressions:

>>>

>>> lambda x: x + 1
1

Decorators

In Python, a decorator is the implementation of a pattern that allows adding a behavior to a function or a class. It is usually expressed with the

>>> lambda x: x
34 syntax prefixing a function. Here’s a contrived example:

>>> lambda x: x + 1
2

In the example above,

>>> lambda x: x
35 is a function that adds a behavior to
>>> lambda x: x
36, so that invoking
>>> lambda x: x
37 results in the following output:

>>> lambda x: x + 1
3

>>> lambda x: x
36 only prints
>>> lambda x: x
39, but the decorator adds an extra behavior that also prints
>>> lambda x: x
40.

A decorator can be applied to a lambda. Although it’s not possible to decorate a lambda with the

>>> lambda x: x
34 syntax, a decorator is just a function, so it can call the lambda function:

>>> lambda x: x + 1
4

>>> lambda x: x
42, decorated with
>>> lambda x: x
43 on line 11, is invoked with argument
>>> lambda x, y: x + y
8 on line 15. By contrast, on line 18, a lambda function is immediately involved and embedded in a call to
>>> lambda x: x
45, the decorator. When you execute the code above you obtain the following:

>>> lambda x: x + 1
5

See how, as you’ve already seen, the name of the lambda function appears as

>>> lambda x: x
06, whereas
>>> lambda x: x
47 is clearly identified for the normal function.

Decorating the lambda function this way could be useful for debugging purposes, possibly to debug the behavior of a lambda function used in the context of a higher-order function or a key function. Let’s see an example with

def add_one[x]:
    return x + 1
4:

>>> lambda x: x + 1
6

The first argument of

def add_one[x]:
    return x + 1
4 is a lambda that multiplies its argument by
>>> full_name = lambda first, last: f'Full name: {first.title[]} {last.title[]}'
>>> full_name['guido', 'van rossum']
'Full name: Guido Van Rossum'
6. This lambda is decorated with
>>> lambda x: x
45. When executed, the example above outputs the following:

>>> lambda x: x + 1
7

The result

>>> lambda x: x
52 is a list obtained from multiplying each element of
>>> lambda x: x
53. For now, consider
>>> lambda x: x
53 equivalent to the list
>>> lambda x: x
55.

You will be exposed to

def add_one[x]:
    return x + 1
4 in more details in Map.

A lambda can also be a decorator, but it’s not recommended. If you find yourself needing to do this, consult PEP 8, Programming Recommendations.

For more on Python decorators, check out Primer on Python Decorators.

Remove ads

Closure

A closure is a function where every free variable, everything except parameters, used in that function is bound to a specific value defined in the enclosing scope of that function. In effect, closures define the environment in which they run, and so can be called from anywhere.

The concepts of lambdas and closures are not necessarily related, although lambda functions can be closures in the same way that normal functions can also be closures. Some languages have special constructs for closure or lambda [for example, Groovy with an anonymous block of code as Closure object], or a lambda expression [for example, Java Lambda expression with a limited option for closure].

Here’s a closure constructed with a normal Python function:

>>> lambda x: x + 1
8

>>> lambda x: x
57 returns
>>> lambda x: x
58, a nested function that computes the sum of three arguments:

  • >>> full_name = lambda first, last: f'Full name: {first.title[]} {last.title[]}'
    >>> full_name['guido', 'van rossum']
    'Full name: Guido Van Rossum'
    
    0 is passed as an argument to
    >>> lambda x: x
    
    57.
  • >>> lambda x: x
    
    61 is a variable local to
    >>> lambda x: x
    
    57.
  • >>> lambda x: x
    
    63 is an argument passed to
    >>> lambda x: x
    
    58.

To test the behavior of

>>> lambda x: x
57 and
>>> lambda x: x
58,
>>> lambda x: x
57 is invoked three times in a
>>> lambda x: x
68 loop that prints the following:

>>> lambda x: x + 1
9

On line 9 of the code,

>>> lambda x: x
58 returned by the invocation of
>>> lambda x: x
57 is bound to the name
>>> lambda x: x
71. On line 5,
>>> lambda x: x
58 captures
>>> full_name = lambda first, last: f'Full name: {first.title[]} {last.title[]}'
>>> full_name['guido', 'van rossum']
'Full name: Guido Van Rossum'
0 and
>>> lambda x: x
61 because it has access to its embedding environment, such that upon invocation of the closure, it is able to operate on the two free variables
>>> full_name = lambda first, last: f'Full name: {first.title[]} {last.title[]}'
>>> full_name['guido', 'van rossum']
'Full name: Guido Van Rossum'
0 and
>>> lambda x: x
61.

Similarly, a

def add_one[x]:
    return x + 1
2 can also be a closure. Here’s the same example with a Python lambda function:

>>> [lambda x: x + 1][2]
3
0

When you execute the code above, you obtain the following output:

>>> [lambda x: x + 1][2]
3
1

On line 6,

>>> lambda x: x
57 returns a lambda and assigns it to to the variable
>>> lambda x: x
71. On line 3, the body of the lambda function references
>>> full_name = lambda first, last: f'Full name: {first.title[]} {last.title[]}'
>>> full_name['guido', 'van rossum']
'Full name: Guido Van Rossum'
0 and
>>> lambda x: x
61. The variable
>>> lambda x: x
61 is available at definition time, whereas
>>> full_name = lambda first, last: f'Full name: {first.title[]} {last.title[]}'
>>> full_name['guido', 'van rossum']
'Full name: Guido Van Rossum'
0 is defined at runtime when
>>> lambda x: x
57 is invoked.

In this situation, both the normal function and the lambda behave similarly. In the next section, you’ll see a situation where the behavior of a lambda can be deceptive due to its evaluation time [definition time vs runtime].

Evaluation Time

In some situations involving loops, the behavior of a Python lambda function as a closure may be counterintuitive. It requires understanding when free variables are bound in the context of a lambda. The following examples demonstrate the difference when using a regular function vs using a Python lambda.

Test the scenario first using a regular function:

>>>

>>> [lambda x: x + 1][2]
3
2

In a normal function,

>>> lambda x: x
85 is evaluated at definition time, on line 9, when the function is added to the list:
>>> lambda x: x
86.

Now, with the implementation of the same logic with a lambda function, observe the unexpected behavior:

>>>

>>> [lambda x: x + 1][2]
3
3

The unexpected result occurs because the free variable

>>> lambda x: x
85, as implemented, is bound at the execution time of the lambda expression. The Python lambda function on line 4 is a closure that captures
>>> lambda x: x
85, a free variable bound at runtime. At runtime, while invoking the function
>>> lambda x: x
89 on line 7, the value of
>>> lambda x: x
85 is
>>> lambda x: x
91.

To overcome this issue, you can assign the free variable at definition time as follows:

>>>

>>> [lambda x: x + 1][2]
3
4

A Python lambda function behaves like a normal function in regard to arguments. Therefore, a lambda parameter can be initialized with a default value: the parameter

>>> lambda x: x
85 takes the outer
>>> lambda x: x
85 as a default value. The Python lambda function could have been written as
>>> lambda x: x
94 and have the same result.

The Python lambda function is invoked without any argument on line 7, and it uses the default value

>>> lambda x: x
85 set at definition time.

Remove ads

Testing Lambdas

Python lambdas can be tested similarly to regular functions. It’s possible to use both

>>> lambda x: x
96 and
>>> lambda x: x
97.

>>> lambda x: x
96

The

>>> lambda x: x
96 module handles Python lambda functions similarly to regular functions:

>>> [lambda x: x + 1][2]
3
5

>>> lambda x: x + 1
00 defines a test case with three test methods, each of them exercising a test scenario for
>>> lambda x: x + 1
01 implemented as a lambda function. The execution of the Python file
>>> lambda x: x + 1
02 that contains
>>> lambda x: x + 1
00 produces the following:

>>> [lambda x: x + 1][2]
3
6

As expected, we have two successful test cases and one failure for

>>> lambda x: x + 1
04: the result is
>>> lambda x, y: x + y
9, but the expected result was
>>> lambda x: x + 1
06. This failure is due to an intentional mistake in the test case. Changing the expected result from
>>> lambda x: x + 1
06 to
>>> lambda x, y: x + y
9 will satisfy all the tests for
>>> lambda x: x + 1
00.

>>> lambda x: x
97

The

>>> lambda x: x
97 module extracts interactive Python code from
>>> lambda x: x + 1
12 to execute tests. Although the syntax of Python lambda functions does not support a typical
>>> lambda x: x + 1
12, it is possible to assign a string to the
>>> lambda x: x + 1
14 element of a named lambda:

>>> [lambda x: x + 1][2]
3
7

The

>>> lambda x: x
97 in the doc comment of lambda
>>> lambda x: x + 1
01 describes the same test cases as in the previous section.

When you execute the tests via

>>> lambda x: x + 1
17, you get the following:

>>> [lambda x: x + 1][2]
3
8

The failed test results from the same failure explained in the execution of the unit tests in the previous section.

You can add a

>>> lambda x: x + 1
12 to a Python lambda via an assignment to
>>> lambda x: x + 1
14 to document a lambda function. Although possible, the Python syntax better accommodates
>>> lambda x: x + 1
12 for normal functions than lambda functions.

For a comprehensive overview of unit testing in Python, you may want to refer to Getting Started With Testing in Python.

Lambda Expression Abuses

Several examples in this article, if written in the context of professional Python code, would qualify as abuses.

If you find yourself trying to overcome something that a lambda expression does not support, this is probably a sign that a normal function would be better suited. The

>>> lambda x: x + 1
12 for a lambda expression in the previous section is a good example. Attempting to overcome the fact that a Python lambda function does not support statements is another red flag.

The next sections illustrate a few examples of lambda usages that should be avoided. Those examples might be situations where, in the context of Python lambda, the code exhibits the following pattern:

  • It doesn’t follow the Python style guide [PEP 8]
  • It’s cumbersome and difficult to read.
  • It’s unnecessarily clever at the cost of difficult readability.

Remove ads

Raising an Exception

Trying to raise an exception in a Python lambda should make you think twice. There are some clever ways to do so, but even something like the following is better to avoid:

>>>

>>> [lambda x: x + 1][2]
3
9

Because a statement is not syntactically correct in a Python lambda body, the workaround in the example above consists of abstracting the statement call with a dedicated function

>>> lambda x: x + 1
22. Using this type of workaround should be avoided. If you encounter this type of code, you should consider refactoring the code to use a regular function.

Cryptic Style

As in any programming languages, you will find Python code that can be difficult to read because of the style used. Lambda functions, due to their conciseness, can be conducive to writing code that is difficult to read.

The following lambda example contains several bad style choices:

>>>

[lambda x: x + 1][2] = lambda 2: 2 + 1
                     = 2 + 1
                     = 3
0

The underscore [

>>> lambda x, y: x + y
3] refers to a variable that you don’t need to refer to explicitly. But in this example, three
>>> lambda x, y: x + y
3 refer to different variables. An initial upgrade to this lambda code could be to name the variables:

>>>

[lambda x: x + 1][2] = lambda 2: 2 + 1
                     = 2 + 1
                     = 3
1

Admittedly, it’s still difficult to read. By still taking advantage of a

def add_one[x]:
    return x + 1
2, a regular function would go a long way to render this code more readable, spreading the logic over a few lines and function calls:

>>>

[lambda x: x + 1][2] = lambda 2: 2 + 1
                     = 2 + 1
                     = 3
2

This is still not optimal but shows you a possible path to make code, and Python lambda functions in particular, more readable. In Alternatives to Lambdas, you’ll learn to replace

def add_one[x]:
    return x + 1
4 and
def add_one[x]:
    return x + 1
2 with list comprehensions or generator expressions. This will drastically improve the readability of the code.

Python Classes

You can but should not write class methods as Python lambda functions. The following example is perfectly legal Python code but exhibits unconventional Python code relying on

def add_one[x]:
    return x + 1
2. For example, instead of implementing
>>> lambda x: x + 1
29 as a regular function, it uses a
def add_one[x]:
    return x + 1
2. Similarly,
>>> lambda x: x + 1
31 and
>>> lambda x: x + 1
32 are properties also implemented with lambda functions, instead of regular functions or decorators:

[lambda x: x + 1][2] = lambda 2: 2 + 1
                     = 2 + 1
                     = 3
3

Running a tool like

>>> lambda x: x + 1
33, a style guide enforcement tool, will display the following errors for
>>> lambda x: x + 1
29 and
>>> lambda x: x + 1
35:

[lambda x: x + 1][2] = lambda 2: 2 + 1
                     = 2 + 1
                     = 3
4

Although

>>> lambda x: x + 1
33 doesn’t point out an issue for the usage of the Python lambda functions in the properties, they are difficult to read and prone to error because of the usage of multiple strings like
>>> lambda x: x + 1
37 and
>>> lambda x: x + 1
38.

Proper implementation of

>>> lambda x: x + 1
29 would be expected to be as follows:

[lambda x: x + 1][2] = lambda 2: 2 + 1
                     = 2 + 1
                     = 3
5

>>> lambda x: x + 1
31 would be written as follows:

[lambda x: x + 1][2] = lambda 2: 2 + 1
                     = 2 + 1
                     = 3
6

As a general rule, in the context of code written in Python, prefer regular functions over lambda expressions. Nonetheless, there are cases that benefit from lambda syntax, as you will see in the next section.

Remove ads

Appropriate Uses of Lambda Expressions

Lambdas in Python tend to be the subject of controversies. Some of the arguments against lambdas in Python are:

  • Issues with readability
  • The imposition of a functional way of thinking
  • Heavy syntax with the
    def add_one[x]:
        return x + 1
    
    2 keyword

Despite the heated debates questioning the mere existence of this feature in Python, lambda functions have properties that sometimes provide value to the Python language and to developers.

The following examples illustrate scenarios where the use of lambda functions is not only suitable but encouraged in Python code.

Classic Functional Constructs

Lambda functions are regularly used with the built-in functions

def add_one[x]:
    return x + 1
4 and
def add_one[x]:
    return x + 1
5, as well as
>>> _[1, 2]
3
3, exposed in the module
>>> lambda x: x + 1
45. The following three examples are respective illustrations of using those functions with lambda expressions as companions:

>>>

[lambda x: x + 1][2] = lambda 2: 2 + 1
                     = 2 + 1
                     = 3
7

You may have to read code resembling the examples above, albeit with more relevant data. For that reason, it’s important to recognize those constructs. Nevertheless, those constructs have equivalent alternatives that are considered more Pythonic. In Alternatives to Lambdas, you’ll learn how to convert higher-order functions and their accompanying lambdas into other more idiomatic forms.

Key Functions

Key functions in Python are higher-order functions that take a parameter

>>> lambda x: x + 1
46 as a named argument.
>>> lambda x: x + 1
46 receives a function that can be a
def add_one[x]:
    return x + 1
2. This function directly influences the algorithm driven by the key function itself. Here are some key functions:

  • >>> _[1, 2]
    3
    
    4: list method
  • >>> _[1, 2]
    3
    
    5,
    >>> _[1, 2]
    3
    
    6,
    >>> _[1, 2]
    3
    
    7: built-in functions
  • >>> lambda x: x + 1
    
    53 and
    >>> lambda x: x + 1
    
    54: in the Heap queue algorithm module
    >>> lambda x: x + 1
    
    55

Imagine that you want to sort a list of IDs represented as strings. Each ID is the concatenation of the string

>>> lambda x: x + 1
56 and a number. Sorting this list with the built-in function
>>> _[1, 2]
3
5, by default, uses a lexicographic order as the elements in the list are strings.

To influence the sorting execution, you can assign a lambda to the named argument

>>> lambda x: x + 1
46, such that the sorting will use the number associated with the ID:

>>>

[lambda x: x + 1][2] = lambda 2: 2 + 1
                     = 2 + 1
                     = 3
8

UI Frameworks

UI frameworks like Tkinter, wxPython, or .NET Windows Forms with IronPython take advantage of lambda functions for mapping actions in response to UI events.

The naive Tkinter program below demonstrates the usage of a

def add_one[x]:
    return x + 1
2 assigned to the command of the Reverse button:

[lambda x: x + 1][2] = lambda 2: 2 + 1
                     = 2 + 1
                     = 3
9

Clicking the button Reverse fires an event that triggers the lambda function, changing the label from Lambda Calculus to suluclaC adbmaL*:

Both wxPython and IronPython on the .NET platform share a similar approach for handling events. Note that

def add_one[x]:
    return x + 1
2 is one way to handle firing events, but a function may be used for the same purpose. It ends up being self-contained and less verbose to use a
def add_one[x]:
    return x + 1
2 when the amount of code needed is very short.

To explore wxPython, check out How to Build a Python GUI Application With wxPython.

Remove ads

Python Interpreter

When you’re playing with Python code in the interactive interpreter, Python lambda functions are often a blessing. It’s easy to craft a quick one-liner function to explore some snippets of code that will never see the light of day outside of the interpreter. The lambdas written in the interpreter, for the sake of speedy discovery, are like scrap paper that you can throw away after use.

>>> lambda x: x + 1
62

In the same spirit as the experimentation in the Python interpreter, the module

>>> lambda x: x + 1
62 provides functions to time small code fragments.
>>> lambda x: x + 1
64 in particular can be called directly, passing some Python code in a string. Here’s an example:

>>>

>>> add_one = lambda x: x + 1
>>> add_one[2]
3
0

When the statement is passed as a string,

>>> lambda x: x + 1
65 needs the full context. In the example above, this is provided by the second argument that sets up the environment needed by the main function to be timed. Not doing so would raise a
>>> lambda x: x + 1
66 exception.

Another approach is to use a

def add_one[x]:
    return x + 1
2:

>>>

>>> add_one = lambda x: x + 1
>>> add_one[2]
3
1

This solution is cleaner, more readable, and quicker to type in the interpreter. Although the execution time was slightly less for the

def add_one[x]:
    return x + 1
2 version, executing the functions again may show a slight advantage for the
>>> lambda x: x + 1
69 version. The execution time of the
>>> lambda x: x + 1
70 is excluded from the overall execution time and shouldn’t have any impact on the result.

Monkey Patching

For testing, it’s sometimes necessary to rely on repeatable results, even if during the normal execution of a given software, the corresponding results are expected to differ, or even be totally random.

Let’s say you want to test a function that, at runtime, handles random values. But, during the testing execution, you need to assert against predictable values in a repeatable manner. The following example shows how, with a

def add_one[x]:
    return x + 1
2 function, monkey patching can help you:

>>> add_one = lambda x: x + 1
>>> add_one[2]
3
2

A context manager helps with insulating the operation of monkey patching a function from the standard library [

>>> lambda x: x + 1
72, in this example]. The lambda function assigned to
>>> lambda x: x + 1
73 substitutes the default behavior by returning a static value.

This allows testing any function depending on

>>> lambda x: x + 1
74 in a predictable fashion. Prior to exiting from the context manager, the default behavior of
>>> lambda x: x + 1
74 is reestablished to eliminate any unexpected side effects that would affect other areas of the testing that may depend on the default behavior of
>>> lambda x: x + 1
74.

Unit test frameworks like

>>> lambda x: x
96 and
>>> lambda x: x + 1
78 take this concept to a higher level of sophistication.

With

>>> lambda x: x + 1
78, still using a
def add_one[x]:
    return x + 1
2 function, the same example becomes more elegant and concise :

>>> add_one = lambda x: x + 1
>>> add_one[2]
3
3

With the pytest

>>> lambda x: x + 1
81 fixture,
>>> lambda x: x + 1
73 is overwritten with a lambda that will return a deterministic value,
>>> lambda x: x + 1
83, allowing to validate the test. The pytest
>>> lambda x: x + 1
81 fixture allows you to control the scope of the override. In the example above, invoking
>>> lambda x: x + 1
73 in subsequent tests, without using monkey patching, would execute the normal implementation of this function.

Executing the

>>> lambda x: x + 1
78 test gives the following result:

>>> add_one = lambda x: x + 1
>>> add_one[2]
3
4

The test passes as we validated that the

>>> lambda x: x + 1
87 was exercised, and the results were the expected ones in the context of the test.

Remove ads

Alternatives to Lambdas

While there are great reasons to use

def add_one[x]:
    return x + 1
2, there are instances where its use is frowned upon. So what are the alternatives?

Higher-order functions like

def add_one[x]:
    return x + 1
4,
def add_one[x]:
    return x + 1
5, and
>>> _[1, 2]
3
3 can be converted to more elegant forms with slight twists of creativity, in particular with list comprehensions or generator expressions.

To learn more about list comprehensions, check out When to Use a List Comprehension in Python. To learn more about generator expressions, check out How to Use Generators and yield in Python.

Map

The built-in function

def add_one[x]:
    return x + 1
4 takes a function as a first argument and applies it to each of the elements of its second argument, an iterable. Examples of iterables are strings, lists, and tuples. For more information on iterables and iterators, check out Iterables and Iterators.

def add_one[x]:
    return x + 1
4 returns an iterator corresponding to the transformed collection. As an example, if you wanted to transform a list of strings to a new list with each string capitalized, you could use
def add_one[x]:
    return x + 1
4, as follows:

>>>

>>> add_one = lambda x: x + 1
>>> add_one[2]
3
5

You need to invoke

>>> lambda x: x + 1
95 to convert the iterator returned by
def add_one[x]:
    return x + 1
4 into an expanded list that can be displayed in the Python shell interpreter.

Using a list comprehension eliminates the need for defining and invoking the lambda function:

>>>

>>> add_one = lambda x: x + 1
>>> add_one[2]
3
6

Filter

The built-in function

def add_one[x]:
    return x + 1
5, another classic functional construct, can be converted into a list comprehension. It takes a predicate as a first argument and an iterable as a second argument. It builds an iterator containing all the elements of the initial collection that satisfies the predicate function. Here’s an example that filters all the even numbers in a given list of integers:

>>>

>>> add_one = lambda x: x + 1
>>> add_one[2]
3
7

Note that

def add_one[x]:
    return x + 1
5 returns an iterator, hence the need to invoke the built-in type
>>> lambda x: x + 1
99 that constructs a list given an iterator.

The implementation leveraging the list comprehension construct gives the following:

>>>

>>> add_one = lambda x: x + 1
>>> add_one[2]
3
8

Reduce

Since Python 3,

def add_one[x]:
    return x + 1
6 has gone from a built-in function to a
>>> lambda x: x + 1
45 module function. As
def add_one[x]:
    return x + 1
4 and
def add_one[x]:
    return x + 1
5, its first two arguments are respectively a function and an iterable. It may also take an initializer as a third argument that is used as the initial value of the resulting accumulator. For each element of the iterable,
def add_one[x]:
    return x + 1
6 applies the function and accumulates the result that is returned when the iterable is exhausted.

To apply

def add_one[x]:
    return x + 1
6 to a list of pairs and calculate the sum of the first item of each pair, you could write this:

>>>

>>> add_one = lambda x: x + 1
>>> add_one[2]
3
9

A more idiomatic approach using a generator expression, as an argument to

>>> [lambda x: x + 1][2]
3
06 in the example, is the following:

>>>

def add_one[x]:
    return x + 1
0

A slightly different and possibly cleaner solution removes the need to explicitly access the first element of the pair and instead use unpacking:

>>>

def add_one[x]:
    return x + 1
1

The use of underscore [

>>> lambda x, y: x + y
3] is a Python convention indicating that you can ignore the second value of the pair.

>>> [lambda x: x + 1][2]
3
06 takes a unique argument, so the generator expression does not need to be in parentheses.

Are Lambdas Pythonic or Not?

PEP 8, which is the style guide for Python code, reads:

Always use a def statement instead of an assignment statement that binds a lambda expression directly to an identifier. [Source]

This strongly discourages using lambda bound to an identifier, mainly where functions should be used and have more benefits. PEP 8 does not mention other usages of

def add_one[x]:
    return x + 1
2. As you have seen in the previous sections, lambda functions may certainly have good uses, although they are limited.

A possible way to answer the question is that lambda functions are perfectly Pythonic if there is nothing more Pythonic available. I’m staying away from defining what “Pythonic” means, leaving you with the definition that best suits your mindset, as well as your personal or your team’s coding style.

Beyond the narrow scope of Python

def add_one[x]:
    return x + 1
2, How to Write Beautiful Python Code With PEP 8 is a great resource that you may want to check out regarding code style in Python.

Conclusion

You now know how to use Python

def add_one[x]:
    return x + 1
2 functions and can:

  • Write Python lambdas and use anonymous functions
  • Choose wisely between lambdas or normal Python functions
  • Avoid excessive use of lambdas
  • Use lambdas with higher-order functions or Python key functions

If you have a penchant for mathematics, you may have some fun exploring the fascinating world of lambda calculus.

Python lambdas are like salt. A pinch in your spam, ham, and eggs will enhance the flavors, but too much will spoil the dish.

Take the Quiz: Test your knowledge with our interactive “Python Lambda Functions” quiz. Upon completion you will receive a score so you can track your learning progress over time:

Take the Quiz »

Note: The Python programming language, named after Monty Python, prefers to use

>>> [lambda x: x + 1][2]
3
12,
>>> [lambda x: x + 1][2]
3
13, and
>>> [lambda x: x + 1][2]
3
14 as metasyntactic variables, instead of the traditional
>>> [lambda x: x + 1][2]
3
15,
>>> [lambda x: x + 1][2]
3
16, and
>>> [lambda x: x + 1][2]
3
17.

Mark as Completed

Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: How to Use Python Lambda Functions

🐍 Python Tricks 💌

Get a short & sweet Python Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.

Send Me Python Tricks »

About Andre Burgaud

Andre is a seasoned software engineer passionate about technology and programming languages, in particular, Python.

» More about Andre

Each tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are:

Aldren

Jon

Joanna

Master Real-World Python Skills With Unlimited Access to Real Python

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

Master Real-World Python Skills
With Unlimited Access to Real Python

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

What Do You Think?

Rate this article:

Tweet Share Share Email

What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.

Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students. Get tips for asking good questions and get answers to common questions in our support portal.

Chủ Đề