Python Lambda Functions

A lambda function in Python is a small, anonymous function defined with the lambda keyword instead of def. It can take any number of arguments, but its body is restricted to a single expression whose result is automatically returned. Lambdas exist for situations where you need a quick, throwaway function—usually to pass as an argument to another function like sorted(), map(), or filter()—without the ceremony of a full function definition.

Overview / How It Works

Every lambda expression creates a genuine function object at runtime, exactly like a def statement does. The difference is purely syntactic: def binds a name to a function and allows a block of statements, while lambda produces an unnamed function object made of one expression. Internally, Python compiles the lambda’s expression into a code object, wraps it in a function object, and gives that object the special name <lambda> (you can see this if you inspect some_lambda.__name__).

Because a lambda is just a function object, it can be stored in a variable, passed around, placed inside a list, or called immediately. It participates in Python’s normal scoping rules (the LEGB rule: Local, Enclosing, Global, Built-in), which means a lambda defined inside another function can access variables from the enclosing scope—this is called a closure. That closure behavior is powerful, but it is also the source of one of the most common lambda bugs, covered later in this lesson.

The key restriction is that a lambda body must be a single expression, not a series of statements. You cannot use = assignment, if/for/while blocks, multiple statements separated by semicolons, or a return keyword inside a lambda. You can, however, use a conditional (ternary) expression like a if condition else b, because that is a single expression, not a statement.

Syntax

lambda arguments: expression
Part Meaning
lambda The keyword that starts the anonymous function definition.
arguments A comma-separated list of parameters, just like in def. Can be empty, use default values, or use *args/**kwargs.
: Separates the parameter list from the body expression.
expression A single expression that is evaluated and implicitly returned. No statements allowed.

Unlike def, a lambda has no parentheses around its parameter list and no explicit return—the value of the expression is the return value.

Examples

Example 1: A lambda vs. an equivalent def function

def square(x):
    return x * x

square_lambda = lambda x: x * x

print(square(5))
print(square_lambda(5))
print(type(square_lambda))

Output:

25
25
<class 'function'>

Both square and square_lambda are ordinary function objects that behave identically when called. The type() call confirms that a lambda is just a function, not a special type.

Example 2: Using a lambda as a sort key

students = [
    ("Amara", 82),
    ("Devon", 91),
    ("Priya", 76),
    ("Liam", 91),
]

# Sort by score descending, then name ascending as a tiebreaker
sorted_students = sorted(students, key=lambda student: (-student[1], student[0]))

for name, score in sorted_students:
    print(f"{name}: {score}")

Output:

Devon: 91
Liam: 91
Amara: 82
Priya: 76

This is the single most common real-world use of lambda: supplying a key function to sorted(), min(), or max(). The lambda receives each tuple and returns a sort key—here, a tuple of (-score, name) so that higher scores come first, and names break ties alphabetically. Writing a full def function just for this one-off key would be unnecessarily verbose.

Example 3: Lambdas with map(), filter(), and a conditional expression

temperatures_celsius = [0, 20, 37, 100, -40]

# Convert to Fahrenheit using map() and a lambda
to_fahrenheit = lambda c: c * 9 / 5 + 32
fahrenheit_temps = list(map(to_fahrenheit, temperatures_celsius))
print(fahrenheit_temps)

# Filter out non-positive temperatures using filter() and a lambda
above_freezing = list(filter(lambda c: c > 0, temperatures_celsius))
print(above_freezing)

# A lambda with a conditional (ternary) expression
classify = lambda c: "hot" if c >= 30 else "mild" if c >= 10 else "cold"
labels = [classify(c) for c in temperatures_celsius]
print(labels)

Output:

[32.0, 68.0, 98.6, 212.0, -40.0]
[20, 37, 100]
['cold', 'mild', 'hot', 'hot', 'cold']

map() applies the lambda to every element and returns an iterator (wrapped in list() here to see the values). filter() keeps only the elements for which the lambda returns a truthy value. The third lambda chains two conditional expressions to classify each temperature—proof that a lambda body can hold fairly rich logic as long as it remains one expression.

How Lambda Functions Work Under the Hood

  • When Python parses a lambda expression, it compiles the expression into a code object, just as it would for a def body, and wraps that code object in a function instance.
  • The resulting object’s __name__ attribute is literally the string '<lambda>', which is why tracebacks involving lambdas are harder to read than ones involving named functions.
  • A lambda captures variables from its enclosing scope by reference, not by value. It does not “freeze” the current value of a variable when it is created—it looks the variable up again each time it is called. This is called late binding and is the root cause of the closure gotcha shown below.
  • Because a lambda is a first-class object, it can be stored in data structures (lists, dicts), returned from other functions, and passed as an argument, exactly like any function created with def.

Common Mistakes

Mistake 1: The late-binding closure trap

A classic bug is creating lambdas in a loop, expecting each one to “remember” the loop variable’s value at creation time:

callbacks = []
for i in range(3):
    callbacks.append(lambda: i)

print([cb() for cb in callbacks])

Output:

[2, 2, 2]

This happens because every lambda looks up i in the enclosing scope when it is called, not when it is defined. By the time the loop finishes, i is 2 for all three lambdas. The fix is to bind the current value as a default argument, since default argument values are evaluated once, at definition time:

callbacks = []
for i in range(3):
    callbacks.append(lambda i=i: i)

print([cb() for cb in callbacks])

Output:

[0, 1, 2]

Mistake 2: Trying to put statements inside a lambda

Because a lambda body must be a single expression, code like assignment, print followed by more statements chained with semicolons in unusual ways, or multi-line logic will fail. For example, writing something like lambda x: x = x + 1 raises a SyntaxError, because assignment is a statement, not an expression, and is not allowed inside a lambda body at all. If your logic needs more than one expression, that is a signal to use a regular def function instead.

Best Practices

  • Use lambdas only for short, throwaway logic passed directly as an argument (e.g., the key for sorted(), or a callback for map()/filter()).
  • Avoid assigning a lambda to a variable, such as square = lambda x: x * x. PEP 8 explicitly discourages this (rule E731)—use def square(x): return x * x instead, since it produces a properly named, more debuggable function.
  • If a lambda’s expression starts to feel hard to read at a glance, that is a sign to refactor it into a named function with def.
  • Prefer a list/dict/generator comprehension over map()/filter() plus a lambda when it improves readability—both approaches are valid, but comprehensions are often more idiomatic in Python.
  • Be careful using lambdas inside loops; remember the late-binding gotcha and bind loop variables with a default argument when needed.
  • For simple operations like addition or attribute access, consider the operator module (e.g., operator.itemgetter) or functools.partial as more explicit alternatives to a lambda.

Practice Exercises

  • Exercise 1: Given the list words = ["banana", "kiwi", "apple", "fig"], use sorted() with a lambda key to sort the words by length, shortest first.
  • Exercise 2: Given numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], use filter() with a lambda to build a list containing only the even numbers, then use map() with a lambda to square each of those even numbers.
  • Exercise 3: Write a lambda assigned to a variable named describe that takes a number and returns the string "negative", "zero", or "positive" depending on its value, using a chained conditional expression. Then explain in your own words why this particular use of assigning a lambda to a name would be better written as a def function.

Summary

  • A lambda is an anonymous function defined with lambda arguments: expression; its body must be exactly one expression, which is implicitly returned.
  • Lambdas are ordinary function objects under the hood—first-class, callable, and scoped just like def functions—but their __name__ is always '<lambda>'.
  • They shine as short, inline arguments to functions like sorted(), min(), max(), map(), and filter().
  • Lambdas use late-binding closures, which can produce surprising results when created inside loops; fix this with a default argument capture.
  • PEP 8 recommends against assigning a lambda to a variable name—use def for anything you plan to name and reuse.