A lambda function is a tiny, unnamed function written in a single expression. It’s useful when you need to pass a small function as an argument and a full def would be overkill.
The syntax
double = lambda x: x * 2
print(double(5)) # 10
Read it as: “a function that takes x and returns x * 2”.
- The
lambdakeyword introduces it. - The parameters come before the colon:
lambda x:orlambda x, y:. - After the colon, a single expression whose value is automatically returned.
This is equivalent to:
def double(x: int) -> int:
return x * 2
Same behaviour, more characters. So why use lambda?
When lambdas shine — passing functions
The real use case is passing a small function to another function. The most common example is sorting:
people: list[dict[str, str | int]] = [
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 25},
{"name": "Carol", "age": 35},
]
by_age = sorted(people, key=lambda p: p["age"])
print(by_age)
[{'name': 'Bob', 'age': 25}, {'name': 'Alice', 'age': 30}, {'name': 'Carol', 'age': 35}]
sorted needs to know “what should I sort by?”. You answer with a tiny function that takes one item and returns the value to sort on. Writing a full def just to extract p["age"] would be excessive.
Other built-ins that take a function argument include map, filter, min, max — all of which we’ll meet in Section 7.
Lambdas have rules
- A lambda is one expression. You can’t put a full statement (like
if,for, or assignment) inside. - It cannot have multiple lines.
- It cannot have type hints in the same form as
def.
These restrictions are intentional. If a function is complicated enough to need multiple lines or if blocks, give it a name and write it with def.
You can still use if/else — as an expression
The ternary form works inside a lambda because it’s an expression, not a statement:
sign = lambda n: "positive" if n > 0 else ("zero" if n == 0 else "negative")
print(sign(3)) # positive
print(sign(0)) # zero
print(sign(-2)) # negative
But once it gets this complex, a regular def is usually clearer.
When NOT to use a lambda
Don’t use a lambda just because it’s shorter than def. Two anti-patterns:
# bad — assigning a lambda to a name. Just use def.
square = lambda x: x * x
# good
def square(x: int) -> int:
return x * x
Ruff will flag the first form. The named-function version is more debuggable (it shows up in stack traces) and supports proper type hints.
# bad — lambda that doesn't do anything new
people.sort(key=lambda p: p["age"]) # OK
people.sort(key=lambda p: int(p["age"])) # OK if needed
# in some libraries you might see something like:
foo = lambda: None # avoid — write a real function
Lambdas in real ML code
You’ll see lambdas in pandas, PyTorch, and other libraries:
# pandas: transform a column
df["doubled"] = df["value"].apply(lambda x: x * 2)
# sort items by length
strings.sort(key=lambda s: len(s))
Both cases are appropriate uses — the operation is one tiny expression, and the function won’t be reused.
Summary
- Use
deffor any function with a name, more than one line, or type hints. - Use
lambdafor tiny one-shot functions passed as arguments. - If in doubt, prefer
def. Named functions are easier to read and debug.
What’s next
Final lesson of the section — type hints and docstrings, the two habits that separate beginner code from professional code.