Embercode
✕
Functions and ScopeLesson 10 of 17

45 min read

Lambdas in Python

Why it matters

Many jobs in a program are small and short-lived. Sort these students by score. Remove the negative numbers from this list. Print each name in capital letters. Each job needs a rule, and the rule is often one line long.

With only def, each rule needs a named function, written somewhere else in the file, even when its body is one line. A lambda is a short function written in the place where it is needed, without a name. It lets you pass a rule to another function the way you pass a number. Lambdas appear most often with sorted, map and filter, which take a rule as input.

This lesson covers how to write a lambda, what a lambda may and may not contain, and the behavior that causes most lambda bugs in Python: a lambda reads outer variables when it runs, not when it is written.

The output in this lesson was produced with Python 3.14.

If you have written lambdas in Java, three differences matter here. A Python lambda holds one expression, not a block. It cannot have type hints. And it looks up outer variables each time it runs.

The idea: a function with no name

A function takes some inputs and gives back a result. A function written with def has a name, and you call it by that name. A lambda is the same idea without a name. Its shape is:

lambda parameters: expression

The word lambda starts it. The parameters come next, separated by commas. A colon follows, and then the expression. An expression is code that gives back a value, such as a + b or len(s). A statement does a job and gives back no value, such as count = 0. The body of a lambda can only hold one expression, and its value is returned automatically. A lambda has no return keyword.

Here is a lambda stored in a name:

Python · editable

Output:

add(3, 4) = 7

Read lambda a, b: a + b as "a function of a and b that gives back a + b". Storing a lambda in a name is fine for a short test. The Python style guide, PEP 8, says to use def when a function has a name, so real projects write named rules with def.

Parts of a lambda

  • Parameters go after lambda, with no brackets: lambda s: len(s). With two or more, separate them with commas: lambda a, b: a + b. With none, write only the word and the colon: lambda: print("hi").
  • A parameter can have a default value, as in def: lambda x, y=10: x + y.
  • The body is one expression after the colon.

Here is the same rule written with def and with lambda:

Python · editable

Output:

5 5

Both do the same job. The def version can hold many lines and has a name you can search for. The lambda holds one expression and has no name.

A lambda can also be called on the spot. Put it in parentheses, then add a second pair of parentheses with the arguments:

Python · editable

Output:

36
hi
15

Lambdas with collections

Python lists do not have methods that take a rule. Three built-in tools do that job:

  • filter(rule, items) keeps each item for which the rule gives back a true value.
  • map(rule, items) gives back the rule's result for each item.
  • sorted(items, key=rule) gives back a new sorted list.

filter and map give back an iterator. An iterator is an object that hands out one item at a time. Wrap it in list(...) to see all the items at once, or loop over it.

A list comprehension builds a new list from a loop, in one line. [name.upper() for name in names] means "for each name, put name.upper() in a new list". Most Python code uses comprehensions for this job, because they are easy to read. Both forms appear in real code, so learn both.

Python · editable

Output:

['ANN', 'BO', 'CHRIS', 'DEE']
['ann', 'chris', 'dee']
['hello ann', 'hello bo', 'hello chris', 'hello dee']

The filter call keeps the names with three or more letters, so bo is left out. The result is a new list, and names is unchanged. To change a list in place, replace its contents with a slice assignment:

names[:] = [s for s in names if len(s) >= 3]

names[:] means "all the positions in the list". Assigning to it replaces the contents of the same list object. A plain names = [...] would point names at a new list, and any other name that pointed to the old list would not see the change.

Sorting with a key

sort and sorted do not take a comparison function that gives back a negative, zero or positive number. Python uses a key instead. A key is a rule that gives back one value for each item, and Python sorts the items by those values.

Python · editable

Output:

['fig', 'kiwi', 'apple', 'banana']
['banana', 'apple', 'fig', 'kiwi']
['banana', 'fig', 'apple', 'kiwi']

w[-1] is the last letter of w. An index of -1 counts from the end of the text. words.sort(...) changes the list and gives back None. sorted(...) gives back a new list and leaves words as it was, as the third line shows. Python's sort is stable: items whose keys are equal keep the order they had before the sort.

If you already have an old-style comparison function, functools.cmp_to_key turns it into a key.

max and min take the same key argument:

Python · editable

Output:

banana

A shorter form: passing an existing function

If the rule only calls a function that already exists, pass that function without a lambda. str.upper is the upper method of the str type, and it takes one string. So map(str.upper, names) does the same job as map(lambda s: s.upper(), names):

Python · editable

Output:

['ANN', 'BO', 'CHRIS', 'DEE']

Use the short form when it reads clearly. When the rule needs two steps, write the lambda.

Worked example 1: a lambda that reads an outer variable

A lambda can use names that were created outside it, in the code that wrote it. A function that keeps a link to variables from the place where it was made is called a closure. Every lambda in this lesson that uses an outer name is a closure.

Python · editable

Output:

True False

len("CHRIS") is 5, and 5 is greater than 4, so the first result is True. The lambda uses threshold with no special marking. Python finds the name in the outer code.

Late lookup: the lambda reads the variable when it runs

This is the rule that matters most. The lambda does not copy the value of threshold when it is written. It looks up the name each time it runs. If the variable changes later, the next call sees the new value.

Python · editable

Output:

True
True False

After threshold becomes 2, "CHRIS" (length 5) is still longer, and "BO" (length 2) is not longer than 2. Python has no rule that stops this. The result always follows the variable's current value.

A lambda can change an object it can see

The lambda can read a name that points to a list, and it can change that list. The name itself is not reassigned, so there is no problem:

Python · editable

Output:

log = ['written', 'written']

log.append changes the list in place. The name log still points to the same list.

Changing the outer variable itself

A lambda cannot assign to an outer variable. An assignment such as count += 1 is a statement, and a lambda body holds only an expression. The mistakes section shows the error. To change an outer variable, write a def and use nonlocal. The word nonlocal says "this name belongs to the function around me, not to this function":

Python · editable

Output:

calls = 3

make_counter creates count once. Each call to counter() changes that same count. The function that make_counter gives back is a closure, like the lambda in the previous example.

Worked example 2: a lambda that builds other lambdas

A function can give back a lambda. The lambda keeps the variables it used, even after the function has finished:

Python · editable

Output:

6 11

Each call to make_adder creates a new amount. Each returned lambda keeps the amount from its own call. This is useful when you want a family of similar rules, each with its own number.

Worked example 3: a multi-key sort

Sometimes a sort needs two steps. Here students are sorted by score, highest first, and by name when two scores are equal. The key gives back a tuple for each student. A tuple is a fixed group of values in parentheses, such as (-72, "Ravi"). Python compares two tuples one item at a time, and the first items that differ decide the order.

Python · editable

Output:

Mira 91
Ola 72
Ravi 72
Ben 58
['Ola', 'Ravi']
Student(name='Mira', score=91)

The -s.score part makes higher scores come first, because a bigger score gives a more negative number. Equal scores are then compared by name, in alphabetical order. reverse=True cannot do this, because it flips the names as well.

@dataclass is a decorator. A decorator is a line that starts with @ and changes the class below it. Here it writes the code that stores name and score when a Student is made, and the code that prints Student(name='Mira', score=91). A dataclass is a class whose main job is to hold values.

defaultdict(list) is a dictionary that makes a value when a key is missing. It calls the function it was given, here list, which makes an empty list. So by_score[s.score].append(s.name) works the first time each score appears. Passing list as a value is the same idea as a lambda, without writing one.

Common mistakes

Writing a statement where an expression is needed

A lambda body must be one expression. A statement is not allowed:

count = 0
inc = lambda: count += 1

Python reports:

  File "/path/to/main.py", line 2
    inc = lambda: count += 1
                        ^^
SyntaxError: invalid syntax

Two fixes are possible. Use a def with nonlocal, as shown above. Or write an expression that gives back a value, such as lambda n: n + 1.

The same error happens with return:

double = lambda x: return x * 2

Python reports:

  File "/path/to/main.py", line 1
    double = lambda x: return x * 2
                       ^^^^^^
SyntaxError: invalid syntax

Remove the return. The value of the expression is returned automatically.

Expecting a lambda to run when it is written

Python · editable

Output:

before the call
hi

Writing lambda: print("hi") does not print anything. The body runs only when the lambda is called, with greet().

Passing the wrong number of arguments

add = lambda a, b: a + b
print(add(1))

Python reports:

Traceback (most recent call last):
  File "/path/to/main.py", line 2, in <module>
    print(add(1))
          ~~~^^^
TypeError: <lambda>() missing 1 required positional argument: 'b'

The message says <lambda> because a lambda has no name.

Expecting each lambda in a loop to keep its own value

Python · editable

Output:

[2, 2, 2]

All three lambdas read the same name, i. When the loop ends, i is 2, and each lambda reads i when it runs. Python gives no error for this. The only sign is a wrong answer.

To give each lambda its own value, pass the current value as a default parameter. A default is set when the lambda is written:

Python · editable

Output:

[0, 1, 2]

Sorting items that cannot be compared

items = [3, "apple", 1]
print(sorted(items, key=lambda x: x))

Python reports:

Traceback (most recent call last):
  File "/path/to/main.py", line 2, in <module>
    print(sorted(items, key=lambda x: x))
          ~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: '<' not supported between instances of 'str' and 'int'

The sort compares keys in pairs, and a number cannot be compared with text. Give every item a key of one type, or sort a list whose items all have one type.

Misspelling an attribute inside a lambda

from dataclasses import dataclass
 
 
@dataclass
class Student:
    name: str
    score: int
 
 
students = [Student("Ravi", 72), Student("Mira", 91)]
students.sort(key=lambda s: s.scroe)
print(students)

Python reports:

Traceback (most recent call last):
  File "/path/to/main.py", line 11, in <module>
    students.sort(key=lambda s: s.scroe)
    ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
  File "/path/to/main.py", line 11, in <lambda>
    students.sort(key=lambda s: s.scroe)
                                ^^^^^^^
AttributeError: 'Student' object has no attribute 'scroe'. Did you mean: 'score'?

The second traceback entry names <lambda>, so you can see that the error comes from inside the rule.

Practice

Each exercise has its answer below it. Try to write your answer before you read it.

Exercise 1

Given numbers = [4, -2, 9, -7], write code that removes every negative number from the list, in place, using filter and a lambda. Then print the list.

Show the answer

Python · editable

Output: [4, 9]. The lambda returns True for numbers that are zero or more, and filter keeps those. The slice assignment changes the list in place.

Exercise 2

Write a function make_multiplier(factor) that gives back a lambda. The lambda multiplies its input by factor. Create triple with make_multiplier(3), and print triple(5).

Show the answer

Python · editable

Output: 15. The returned lambda keeps its own factor, which is 3.

Exercise 3

The code below prints [2, 2, 2], not [0, 1, 2]. Explain why, and change the code so that it prints [0, 1, 2].

funcs = [lambda: i for i in range(3)]
print([f() for f in funcs])

Show the answer

All three lambdas read the same name i. The loop ends with i set to 2, and each lambda reads i when it is called. The fix is to give each lambda its own value as a default parameter:

Python · editable

Output: [0, 1, 2].

Exercise 4

Sort the words ["pear", "apple", "fig"] by their last letter, using a lambda. Print the result.

Show the answer

Python · editable

Output: ['apple', 'fig', 'pear']. The last letters are r, e and g. In order, e comes first, then g, then r.

Exercise 5

Write a lambda that takes a string and gives back True when the string contains the letter a. Test it on "banana" and "kiwi".

Show the answer

Python · editable

Output:

True
False

Summary

  • A lambda is an unnamed function written as lambda parameters: expression. It holds one expression, and that value is returned.
  • sorted, sort, max, min, filter and map take lambdas that describe each step.
  • A lambda can read names from the function that created it, and it reads their current values each time it runs.
  • A lambda cannot assign to an outer variable. Use a def with nonlocal for that.
  • The most common silent bug is the late lookup in a loop. Pass the loop value as a default parameter to avoid it.

The next lesson covers generic functions, which let one function work with many types.

Check yourself: Static methods and lambdas in Python (8 questions) →Practice this topic: coding problems for Functions and Scope →

Stuck? Ask the tutor

Ask about anything in this lesson. Answers are short and use simple words.

Sign in to ask the tutor.

← Static methods and helper modules in Python