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 lambda keyword introduces it.
  • The parameters come before the colon: lambda x: or lambda 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 def for any function with a name, more than one line, or type hints.
  • Use lambda for 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.

Toggle theme (T)