Lambda functions and when to use them
Overview
A `lambda` creates an anonymous function — a function without a `def` or a name — using a single expression: lambda parameters: expression The expression is evaluated and returned automatically. Lambdas are most commonly passed inline as the `key` or transform argument to built-ins like `sorted()` or `map()`. records = [{'name': 'bob', 'score': 82}, {'name': 'alice', 'score': 91}] sorted(records, key=lambda r: r['score']) # sort by score sorted(records, key=lambda r: -r['score']) # sort descending A `lambda` is strictly for single expressions. They cannot contain statements, assignments, or standard `if`/`else` blocks (though the ternary form `lambda x: x if x > 0 else 0` is allowed). When logic requires multiple steps, use a named `def` instead. Assigning a `lambda` to a variable defeats its purpose and violates PEP8 style guidelines (E731). Since lambdas lack a proper `__name__` attribute, stack traces will only show `<lambda>`, making debugging harder: # ❌ Bad: PEP8 E731 key_fn = lambda r: r['score'] # ✅ Good: Use a real function def key_fn(r): return r['score'] Another major trap is "late binding" when creating lambdas inside a loop. Lambdas capture variables by reference, not by value, meaning all lambdas in a list will use the final value of the loop variable: # ❌ Trap: All lambdas return 2 fns = [lambda: i for i in range(3)] # ✅ Fix: Bind the variable as a default argument fns = [lambda i=i: i for i in range(3)] Think of a `lambda` as a disposable, inline `def` that exists only in the expression it is written in.
Built-ins like `sorted()`, `min()`, `max()`, and `map()` accept a `key` or function argument. Lambdas let you supply a short transform inline without naming it, keeping the call site compact. When the transform is reused or complex, a named function is cleaner.
Where used: `sorted()` and `min()` key arguments on lists of dicts, FastAPI middleware order functions, pandas/data pipeline row transforms
Why learn this
- Sorting lists of API response dicts by a field without writing a separate named `key` function
- Passing inline transforms to `map()`, `filter()`, and `sorted()` in data-processing pipelines
Code walkthrough
records = [
{'endpoint': '/users', 'latency': 45},
{'endpoint': '/health', 'latency': 12},
{'endpoint': '/items', 'latency': 38},
]
sorted_records = sorted(records, key=lambda r: r['latency'])
for r in sorted_records:
print(r['endpoint'], r['latency'])
Focus: `key=lambda r: r['latency']` is passed inline — `sorted()` calls this function on each record and orders by the returned value without needing a named function.
Common mistakes
- Assigning a lambda to a name: `fn = lambda x: x * 2` is semantically identical to `def fn(x): return x * 2`, but a lambda's `__name__` is simply `'<lambda>'` (not a descriptive name), so stack traces are harder to read. Use `def` when you need a reusable named function.
- Putting statements inside a lambda: Lambdas only allow a single expression. Assignments and standard `if`/`else` blocks (not ternary) are statements and raise a `SyntaxError` inside a `lambda`. Use a `def` block instead.
- Late binding in a loop-created lambda: In `fns = [lambda: i for i in range(3)]`, all three lambdas return `2` because they capture the variable `i`, not its value at creation time. Fix this by using a default argument: `lambda i=i: i`.
Glossary
- anonymous function
- A small function that is defined without a name, typically used for short, throwaway tasks. Example: `lambda x: x + 1`.
- ternary form
- A compact, one-line way to write an `if`-`else` statement, evaluating to one value if true and another if false. Example: `'yes' if x > 0 else 'no'`.
Recall questions
- What is the syntax of a `lambda`, and what does it return?
- What is the main restriction on a `lambda` body?
- Why does PEP8 discourage assigning a `lambda` to a variable?
- When should you use a named `def` instead of a `lambda`?
Understanding checks
What is the output?
`["apple", "banana", "cherry"]`
The `sorted()` function uses the `lambda` to determine the sorting key. The `lambda` returns the length of each word, so the words are sorted by their lengths. Since `"apple"` (5) is the shortest, it comes first. `"banana"` and `"cherry"` both have length 6, so they retain their original relative order (stable sort).
Why does this `lambda` definition cause a `SyntaxError`?
Lambdas can only contain a single expression, not statements like assignments.
The `result = x * 2` is an assignment statement, which is not allowed inside a `lambda`. If you need multiple steps or assignments, you must use a `def` function.
What is the output?
`[4, 4, 4]`
This is the late binding trap. All three lambdas capture a reference to the loop variable `i`, not its value at creation time. By the time the lambdas are called, the loop has finished and `i` is `2`. So all three lambdas multiply by `2`.
Practice tasks
Modify Sorting to Use Lambda
The `sort_users` function currently uses a named helper function to sort users by their `'created_at'` date. Modify the code to remove the helper function and instead pass an inline `lambda` to `sorted()`.
Challenge
Multi-key sort
Write `rank_users(users)` that takes a list of user dicts with `'role'` and `'score'` keys and returns them sorted: admins first (`role='admin'`), then by score descending within each group. Use `sorted()` with a single `lambda` that returns a tuple — Python sorts tuples element-by-element.
Continue learning
Previous: Defining functions and return values
Previous: LEGB rule: variable scope in Python
Next: First-class functions: passing and returning functions
Next: List Comprehensions