A lambda is a one-expression function without a name. The syntax is lambda args: expr, and the result is a callable object just like a regular def. Lambdas are mostly used inline: as the key argument to sorted, min and max; as simple callbacks; as predicates for filter; as short transforms for map.
The defining restrictions are that a lambda's body must be a single expression (no statements, no assignments) and that it has no name. Those two limits keep lambdas small on purpose. The moment you want more logic or a descriptive name, switch to def. Large, multi-line lambdas hidden inside argument lists are one of the easiest ways to write code that is hard to read six months later.
Where lambdas really earn their keep is as short sort keys. sorted(rows, key=lambda r: r[1]) sorts by the second field without any helper. sorted(rows, key=lambda r: (r[1], -r[0])) sorts by the second field ascending and breaks ties by the first field descending. This is the idiomatic Python way to do custom ordering.
There's nothing "functional" about lambdas in Python in a deep sense — they are ordinary callables that happen to be spelled compactly. Treat them as syntactic sugar for trivial helpers, and reach for def for anything larger.
Using lambda as a key function
sorted, min, max, itertools.groupby and many others accept a key= argument. A lambda is the shortest way to specify it when the key is a derived value: max(items, key=lambda x: x.score), sorted(words, key=lambda w: len(w)).
For single-attribute access, operator.attrgetter and operator.itemgetter are slightly faster and more self-documenting than a lambda: sorted(items, key=attrgetter("score")). Use them when you only need attribute or item access.
When a def is clearer
Any lambda longer than a simple expression is a sign to switch. A named def adds a docstring, a type hint, and a stack-trace-friendly name — all worth the extra three lines.
Also avoid assigning a lambda to a variable: inc = lambda x: x + 1 is worse than def inc(x): return x + 1 because the former gives you a function whose name is <lambda> in error messages.
Small-function tools you will see together with lambdas.
| Tool | Purpose |
|---|---|
lambda args: exprsyntax | One-expression anonymous function. |
sorted(seq, key=...)built-in | Sorts with a custom key function. |
min / max(seq, key=...)built-in | Pick the smallest/largest by a key. |
itemgetter(i)factory | Faster alternative to lambda for position access. |
attrgetter("name")factory | Attribute-access version of itemgetter. |
filter(pred, it)built-in | Keeps items where a predicate is true. |
functools.reducefunction | Folds an iterable with a two-argument function. |
functools.partialfunction | Binds arguments to a callable. |
Creating Small Anonymous Functions code example
The script below contrasts lambdas with their named equivalents and shows where each form shines.
# Lesson: Creating Small Anonymous Functions
from functools import reduce
from operator import itemgetter, attrgetter
from typing import NamedTuple
class Book(NamedTuple):
title: str
year: int
rating: float
books = [
Book("Fluent Python", 2022, 4.7),
Book("Effective Python", 2019, 4.5),
Book("Python Cookbook", 2013, 4.3),
]
# Sort with lambda (ad-hoc key)
by_year = sorted(books, key=lambda b: b.year)
# Sort with attrgetter (cleaner for plain attribute access)
by_rating = sorted(books, key=attrgetter("rating"), reverse=True)
print("by year:", [b.title for b in by_year])
print("by rating:", [b.title for b in by_rating])
# min/max with a derived key
newest = max(books, key=lambda b: b.year)
longest_title = max(books, key=lambda b: len(b.title))
print("newest:", newest.title)
print("longest title:", longest_title.title)
# filter + lambda; often replaced by a comprehension
high_rated = list(filter(lambda b: b.rating > 4.4, books))
high_rated_comp = [b for b in books if b.rating > 4.4]
assert high_rated == high_rated_comp
print("high rated:", [b.title for b in high_rated])
# reduce: fold a list down to a single value
total = reduce(lambda acc, b: acc + b.rating, books, 0.0)
print(f"rating total: {total:.1f}")
# itemgetter on a list of tuples
rows = [("ana", 91), ("ben", 78), ("cai", 85)]
print("by score asc:", sorted(rows, key=itemgetter(1)))
Things to notice:
1) `key=lambda b: b.year` is the shortest way to sort by an attribute.
2) `attrgetter` is faster and more explicit for plain attribute access.
3) `filter(lambda ...)` and list comprehensions are interchangeable; the latter reads better.
4) `reduce` is fine for concise folds but explicit loops are usually clearer.
Practice replacing named one-liners with lambdas.
words = ["pear", "fig", "apple", "banana"]
print(sorted(words, key=lambda w: (len(w), w))) # shortest first, then alphabetical
print(min(words, key=lambda w: w[-1])) # alphabetically-last letter wins
pairs = [(3, "c"), (1, "a"), (2, "b")]
print(sorted(pairs, key=lambda p: p[0])) # by number
Core sanity checks.
square = lambda x: x * x
assert square(4) == 16
assert sorted([3, 1, 2], key=lambda n: -n) == [3, 2, 1]
from operator import itemgetter
assert sorted([(1, "b"), (2, "a")], key=itemgetter(1)) == [(2, "a"), (1, "b")]
Running prints:
by year: ['Python Cookbook', 'Effective Python', 'Fluent Python']
by rating: ['Fluent Python', 'Effective Python', 'Python Cookbook']
newest: Fluent Python
longest title: Python Cookbook
high rated: ['Fluent Python', 'Effective Python']
rating total: 13.5
by score asc: [('ben', 78), ('cai', 85), ('ana', 91)]