Home Tutorials Functions

Functions

Lambda Functions in Python: Anonymous One-Liners and Where They Shine

Pyford Notes July 1, 2026 7 min read
Key points
  • lambda args: expression returns a function object equivalent to a one-line def.
  • The most common use case is as a key= argument to sorted(), min(), and max().
  • Lambdas cannot contain statements, assignments, or multiple expressions.
  • When the body gets complex or reusable, replace the lambda with a named function.

The lambda syntax

A lambda creates an anonymous function in a single expression. The keyword is followed by a comma-separated parameter list, a colon, and exactly one expression whose value is automatically returned:

  1. Write the lambda keyword.
  2. List zero or more parameter names, separated by commas.
  3. Write a colon (:).
  4. Write a single expression — this is the implicit return value.
# Equivalent pairs

# Named function
def square(x):
    return x * x

# Lambda
square = lambda x: x * x

print(square(5))   # 25

# Multi-argument lambda
add = lambda a, b: a + b
print(add(3, 4))   # 7

# No-argument lambda (unusual but valid)
greeting = lambda: "Hello!"
print(greeting())  # Hello!

Assigning a lambda to a name like square = lambda x: x * x is technically valid but stylistically discouraged. If you are naming the function, use def — it gives you a proper docstring slot and a readable __name__. Lambdas shine when they are used inline and discarded immediately.

Sorting with key=lambda

The key parameter of sorted(), list.sort(), and similar functions accepts any callable that maps a single element to its sort key. Lambdas let you write that mapping inline without declaring a separate function:

people = [
    {"name": "Carol", "age": 32},
    {"name": "Alice", "age": 25},
    {"name": "Bob",   "age": 29},
]

# Sort by age
by_age = sorted(people, key=lambda p: p["age"])
print(by_age[0]["name"])  # Alice

# Sort by name length, then alphabetically
by_name_len = sorted(people, key=lambda p: (len(p["name"]), p["name"]))

For attribute access on objects, operator.attrgetter("attr") is a faster alternative. For dictionary key access, operator.itemgetter("key") avoids the lambda entirely and is slightly more efficient for large datasets.

min(), max(), and lambda

min() and max() accept the same key= argument, which lets you find extremes based on a derived value without pre-sorting:

products = [
    ("Widget",  9.99),
    ("Gadget", 24.50),
    ("Doohickey", 4.75),
]

cheapest = min(products, key=lambda item: item[1])
print(cheapest)  # ("Doohickey", 4.75)

longest_name = max(products, key=lambda item: len(item[0]))
print(longest_name)  # ("Doohickey", 4.75)

Conditional expressions inside lambdas

Because Python's ternary operator (value_if_true if condition else value_if_false) is an expression, not a statement, it fits inside a lambda body:

clamp = lambda x, lo, hi: lo if x < lo else (hi if x > hi else x)

print(clamp(5, 0, 10))   # 5
print(clamp(-3, 0, 10))  # 0
print(clamp(15, 0, 10))  # 10

When conditional logic starts nesting like this, the readability cost of a lambda is usually not worth it. A named function with explicit if / elif / else is far easier to read and test.

map() and filter()

map(func, iterable) applies a function to every element; filter(func, iterable) keeps elements for which the function returns a truthy value. Both return lazy iterators:

numbers = [1, 2, 3, 4, 5, 6, 7, 8]

doubled = list(map(lambda x: x * 2, numbers))
print(doubled)  # [2, 4, 6, 8, 10, 12, 14, 16]

evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens)    # [2, 4, 6, 8]

In modern Python, list comprehensions and generator expressions tend to replace most map() and filter() calls because they read left-to-right in the natural order of thought: [x * 2 for x in numbers] and [x for x in numbers if x % 2 == 0]. The functional forms remain useful when passing a callable object rather than inline logic.

What lambdas cannot do

A lambda body must be a single expression. The following are all illegal inside a lambda and require a def instead:

# NOT valid in a lambda:

# Assignments
# lambda x: result = x * 2   # SyntaxError

# Statements (if/for/while/try)
# lambda x: if x > 0: return x   # SyntaxError

# Multiple expressions
# lambda x: x * 2; x + 1         # SyntaxError

# Walrus operator (:=) works, but rarely helps readability
process = lambda x: (y := x * 2, y + 1)[1]

Lambdas also cannot have type annotations on their parameters, they produce unhelpful <lambda> tracebacks, and they cannot carry a docstring. These limitations are by design — if you need any of those things, use def.

lambda vs def: a decision guide

CriterionUse lambdaUse def
Will it be named and reused?NoYes
Body complexitySingle short expressionMultiple lines or statements
Needs type hints?NoYes
Needs a docstring?NoYes
Used as key= in sorted/min/maxIdealOverkill
Needs clear traceback name?NoYes
PEP 8 guidance The Python style guide discourages assigning a lambda expression to a variable name. If you catch yourself writing f = lambda x: ..., rename it to a proper def f(x): ... block. The lambda form saves one line at the cost of inspectability, testability, and clarity.