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: expressionThe 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:
Output:
add(3, 4) = 7Read 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:
Output:
5 5Both 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:
Output:
36
hi
15Lambdas 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.
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.
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:
Output:
bananaA 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):
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.
Output:
True Falselen("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.
Output:
True
True FalseAfter 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:
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":
Output:
calls = 3make_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:
Output:
6 11Each 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.
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 += 1Python reports:
File "/path/to/main.py", line 2
inc = lambda: count += 1
^^
SyntaxError: invalid syntaxTwo 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 * 2Python reports:
File "/path/to/main.py", line 1
double = lambda x: return x * 2
^^^^^^
SyntaxError: invalid syntaxRemove the return. The value of the expression is returned automatically.
Expecting a lambda to run when it is written
Output:
before the call
hiWriting 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
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:
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
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
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:
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
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
Output:
True
FalseSummary
- A lambda is an unnamed function written as
lambda parameters: expression. It holds one expression, and that value is returned. sorted,sort,max,min,filterandmaptake 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
defwithnonlocalfor 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.