Python Lambda Functions Explained: The 5 Legitimate Uses (and When to Use def Instead)
Most Python tutorials teach lambda syntax and then stop. That's how you end up with codebases full of lambda x: x style one-liners doing the work of a proper def. This post covers the opposite: exactly where lambdas belong, exactly where they don't, and why PEP 8 explicitly tells you to name your function most of the time.
By the end you'll know the 5 concrete places lambda actually beats def, and you'll stop reaching for it out of habit everywhere else.
The syntax
A lambda is a single-expression, anonymous function. That's the whole language feature.
Two hard constraints. First: exactly one expression. You cannot put a statement inside a lambda, so no if:, no for:, no return, no assignments (except for the walrus operator :=, which is technically an expression). Second: no name. The function has __name__ == "<lambda>", which shows up in stack traces as an unhelpful blank.
Both constraints exist for a reason. The whole point of lambda is inline throwaway logic that doesn't need a name because you're passing it directly somewhere.
Lambda vs def: performance
They compile to nearly identical bytecode. Any performance argument for one over the other is folklore. Pick by readability, not speed.
The def version emits the same 4 opcodes plus a STORE_NAME for the function name. Millionths of a second.
The 5 legitimate uses
1. Sort key
The single most common good use. sorted and list.sort take a key= callable that gets called once per element to produce the comparison value.
Naming this function def get_age(u): return u["age"] on the line above adds noise without adding meaning. The lambda IS the meaning.
Same story with min, max, heapq.nsmallest, itertools.groupby, and Pandas' sort_values(key=...).
2. filter / map inside a comprehension or one-liner
When you genuinely need a one-shot transform:
Honestly, most of the time a list comprehension is cleaner ([x * x for x in range(10)]). But when you're chaining into an existing pipeline that expects a callable, lambda fits.
3. GUI / event callbacks
Tkinter, curses, and any event-loop UI framework expects callables for click handlers. When the handler is a one-liner that needs to close over a variable:
The l=label default-argument trick captures the current value, avoiding the classic late-binding closure bug. Without lambda, you'd need a factory function for every loop iteration.
4. Decorator arguments
When a decorator itself takes a callable and you want to configure it inline:
The on=lambda err: ... predicate is short-lived and specific to this call site. A named function would live in module scope and clutter the file.
5. Pandas .apply and column ops
Data-analysis code is lambdas' native habitat:
Naming these adds nothing. The lambda body IS the column-transform semantic.
The 3 anti-patterns to kill
1. Assigning a lambda to a variable
If you're doing this:
Use def instead:
They're the same amount of code, but the def version gives you a real __name__, works with debuggers, and doesn't confuse readers looking for the function definition. PEP 8 says exactly this. Ruff has a rule for it (E731).
2. Multi-line lambdas via nested expressions
Python does not have multi-statement lambdas. When you feel the urge to write:
Just write a def. The tuple-of-expressions trick works but is unreadable.
3. Lambda when a stdlib callable exists
You almost never need to write lambda x: x.lower() when str.lower (bound-method-style) works:
Same for abs, len, str, int, operator.itemgetter, operator.attrgetter. Reach for these first, lambda second.
When def always wins
The moment your function needs a docstring, does anything that isn't a single expression, or gets called from more than one place β use def. The named function shows up in profilers, in stack traces, in help(), and in code review as a distinct thing to review.
FAQ
Is a lambda faster than a def function?
No. They compile to nearly identical bytecode. Choose by readability, not speed.
Can a lambda have multiple statements?
No. A lambda is a single expression. If you need statements (if, for, return, assignments), use def. The walrus operator := is technically an expression and works inside a lambda.
Why does PEP 8 tell me not to assign lambdas to variables?
Because it hides the function from every tool that reads __name__: debuggers, tracebacks, profilers, help(). The equivalent def is the same amount of code and reads much better.
When should I prefer str.lower over lambda w: w.lower()?
Almost always. Unbound methods and stdlib callables (abs, len, str.lower, operator.itemgetter) are shorter and clearer than the equivalent lambda. Use lambda only when no such callable exists.
Do lambdas work in async code?
Yes, but you can't await inside a lambda (await is a statement). For most async callbacks, define an async def. If the callback is fully synchronous and you're passing it to something like asyncio.get_event_loop().call_later, a plain lambda is fine.
Comprehensions are the most idiomatic Python transformation tool β our list comprehension builder shows how to convert a for-loop into one live. Or start Foundations to build the muscle memory across every language feature.