Skip to content
1stepGrowLearnCompareGrow

Python Lambda Function: Where It Helps and When to Use def

A Python lambda function is a one-expression anonymous function. Learn where it helps (sort keys, callbacks), when def wins, and the loop bug it causes.

Ayushi Kulshreshta

AI Engineer

9 min readUpdated
Share
On this page

A Python lambda function is a small anonymous function written as a single expression: lambda x: x ** 2. It returns the value of that expression automatically. Use it where a short function is passed straight to another function, such as a sort key, and use def everywhere else.

Lambdas look simple, which is how they cause trouble. Three lambdas built in a loop can all return 12 when you expected 10, 11 and 12, and a lambda over a large DataFrame can be far slower than one vectorised line. This guide is for Python learners who already write functions with def and want to know when the short form is worth it. It covers the best uses, when def is clearer, the loop gotcha, and the tools that often replace a lambda.

Lambdas are the last stop in the functions series; if def, arguments and scope are still new, start with user-defined Python functions.

What is a lambda in Python?

A lambda is a function without a name, limited to a single expression. The official tutorial's lambda expressions section describes them as small anonymous functions:

Python
square = lambda x: x ** 2       # works, but see below
square(4)                        # 16

# equivalent
def square(x):
    return x ** 2

The return is implicit. As a result, there is no room for statements, loops, assignments, annotations or a docstring. Writing lambda x: return x is a SyntaxError.

That constraint is the whole design. Lambdas exist for the case where naming the function would be more noise than signal.

Lambda vs def at a glance

lambda def
Kind expression statement
Body one expression any number of statements
Return implicit explicit return
Name in tracebacks <lambda> the real function name
Docstring and type hints no yes
Best for short callbacks and key functions everything else

Where does a Python lambda function actually help?

Sort keys are the single best use:

Python
people = [("Ananya", 28), ("Rohit", 35), ("Meera", 24)]

sorted(people, key=lambda p: p[1])              # by age
# [('Meera', 24), ('Ananya', 28), ('Rohit', 35)]
sorted(people, key=lambda p: p[1], reverse=True)
# [('Rohit', 35), ('Ananya', 28), ('Meera', 24)]
sorted(people, key=lambda p: (p[1], p[0]))      # age, then name

That last one — a tuple key for multi-level sorting — is a pattern worth remembering. The sorting HOWTO notes that the key function is called exactly once per item, so even a lambda key stays fast.

Sorting dictionaries and objects works the same way:

Python
scores = {"Ananya": 91, "Rohit": 78, "Meera": 85}
sorted(scores.items(), key=lambda kv: kv[1], reverse=True)
# [('Ananya', 91), ('Meera', 85), ('Rohit', 78)]

from collections import namedtuple
Employee = namedtuple("Employee", "name salary")
employees = [Employee("Ananya", 95000), Employee("Rohit", 72000)]
sorted(employees, key=lambda e: e.salary)
# [Employee(name='Rohit', salary=72000), Employee(name='Ananya', salary=95000)]

However, you do not always need a lambda. When a named function already does the job, pass it directly:

Python
words = ["kiwi", "Banana", "fig", "apple"]
sorted(words)                   # ['Banana', 'apple', 'fig', 'kiwi'] - uppercase first
sorted(words, key=str.lower)    # ['apple', 'Banana', 'fig', 'kiwi']
max(scores, key=scores.get)     # 'Ananya' - the key with the highest value

min, max and aggregates:

Python
records = [{"name": "a", "score": 90}, {"name": "b", "score": 75}]
max(records, key=lambda r: r["score"])     # {'name': 'a', 'score': 90} - the whole record
min(words, key=len)                         # 'fig'

Returning the item rather than the value is why key= beats a comprehension here.

pandas apply, for small transformations:

Python
df["initial"] = df["name"].apply(lambda s: s[0].upper())
df["band"] = df["score"].apply(lambda x: "high" if x > 80 else "low")

Note the performance caveat below, though.

Callbacks and default factories:

Python
from collections import defaultdict
counts = defaultdict(lambda: 0)
nested = defaultdict(lambda: defaultdict(list))
nested["north"]["q1"].append(1200)

defaultdict(lambda: defaultdict(list)) is an idiomatic use, because you need a callable and naming it would not help.

When should you use def instead?

Never assign a lambda to a name. PEP 8's programming recommendations say to always use def instead, and the reason is practical:

Python
# don't
calculate_tax = lambda amount, rate: amount * rate

# do
def calculate_tax(amount, rate):
    return amount * rate

The def version has a real __name__, so tracebacks say calculate_tax rather than <lambda>. In a stack trace full of <lambda> entries, that matters.

Use def when the logic needs explaining:

Python
# unreadable
process = lambda r: (r["a"] * 2 + r["b"]) / (r["c"] if r["c"] else 1)

# clear
def normalised_score(record):
    """Combine a and b, normalised by c (guarding division by zero)."""
    combined = record["a"] * 2 + record["b"]
    divisor = record["c"] or 1
    return combined / divisor

normalised_score({"a": 3, "b": 4, "c": 0})    # 10.0

Use def when you need more than an expression. There is no try, no loop and no assignment statement inside a lambda. So if you are reaching for a workaround, use def.

Why do lambdas created in a loop share one value?

Because a closure looks up the loop variable when it runs, not when it is created. This catches everyone once:

Python
funcs = []
for i in range(3):
    funcs.append(lambda x: x + i)

[f(10) for f in funcs]      # [12, 12, 12]  - not [10, 11, 12]

Closures look up the variable when they run, not its value when they were created. By the time the lambdas run, i is 2. The Python FAQ covers this exact case: why lambdas defined in a loop all return the same result. The same thing happens with def inside a loop, so it is not a lambda-only quirk.

Bind the value explicitly with a default argument, which is evaluated at definition time:

Python
funcs = [lambda x, i=i: x + i for i in range(3)]
[f(10) for f in funcs]      # [10, 11, 12]

Alternatively, use functools.partial, which is clearer about intent:

Python
from functools import partial

def add(x, i):
    return x + i

funcs = [partial(add, i=i) for i in range(3)]
[f(10) for f in funcs]      # [10, 11, 12]

Should you use map and filter with a lambda?

Usually not. Lambdas are often taught alongside map and filter, but in Python a comprehension usually reads better:

Python
nums = [1, 2, 3, 4, 5]

list(map(lambda x: x ** 2, nums))              # [1, 4, 9, 16, 25]
[x ** 2 for x in nums]                          # same, more idiomatic

list(filter(lambda x: x % 2 == 0, nums))        # [2, 4]
[x for x in nums if x % 2 == 0]                 # same, more idiomatic

map earns its place when you already have a named function and no transformation to express:

Python
lines = [" a ", "b  "]
list(map(str.strip, lines))       # ['a', 'b'] - no lambda needed
list(map(int, ["1", "2"]))        # [1, 2]

When is operator.itemgetter better than a lambda?

Whenever the lambda only fetches an item or an attribute. For simple attribute or index access, the operator module is clearer, and the sorting HOWTO describes its accessors as easier and faster:

Python
from operator import itemgetter, attrgetter

sorted(people, key=itemgetter(1))               # instead of lambda p: p[1]
sorted(employees, key=attrgetter("salary"))     # instead of lambda e: e.salary
sorted(records, key=itemgetter("score", "name"))  # multi-key

Are lambdas slow in pandas apply?

The lambda itself is not slow, but apply with a lambda runs Python code once per row. On large frames, therefore, use vectorised operations instead:

Python
# slow on a million rows
df["total"] = df.apply(lambda r: r["price"] * r["qty"], axis=1)

# fast - vectorised
df["total"] = df["price"] * df["qty"]

# conditional, vectorised
import numpy as np
df["band"] = np.where(df["score"] > 80, "high", "low")

The lambda is not the problem; row-wise Python is. Our NumPy broadcasting guide covers why vectorisation is so much faster.

Common mistakes with lambdas

  • Assigning a lambda to a name instead of writing def.
  • Late binding in loops, which gives every lambda the final loop value.
  • Wrapping an existing function, as in key=lambda s: s.lower() when key=str.lower works.
  • Cramming conditionals and arithmetic into one unreadable line.
  • Using apply with a lambda on large DataFrames when a vectorised expression exists.

When should you use a Python lambda function?

Use a Python lambda function when it is an argument to another function and the logic fits comfortably on one line. Everywhere else, use def.

The Sunday Growth Brief

One email a week: the best new comparisons, a fresh roadmap and the tech news worth your attention.

No spam. Unsubscribe in one click.

User-defined functions covers function design properly. Iterators and generators covers the lazy-evaluation companion.

Frequently asked questions

What is the difference between lambda and def?

A lambda is an expression that produces a function, limited to a single expression with an implicit return. def is a statement that can contain any code, accepts annotations and a docstring, and gives the function a real name that appears in tracebacks. Both create the same kind of function object, so anything a lambda does, def can do too.

Should I assign a lambda to a variable?

No. PEP 8 says to always use a def statement instead of binding a lambda directly to a name. You get the same function object with a meaningful __name__ for debugging, and you keep the option to add a docstring and type hints later. Named lambdas save one line and cost clarity, so they are a habit worth dropping.

Why do lambdas in a loop all return the same value?

Because closures look up variables when the function runs, not when it is defined. All the lambdas share the loop variable, which holds its final value by the time they are called. Bind the current value with a default argument, as in lambda x, i=i: x + i, or use functools.partial. Functions defined with def in a loop behave the same way.

Are lambdas slower than regular functions?

No. A lambda and an equivalent def compile to essentially the same bytecode, so calling them costs the same. Any perceived difference comes from how they are used: calling a Python function once per row over a large DataFrame is slow whether it is a lambda or a def, and vectorised operations are the real fix.

Written by

Ayushi Kulshreshta

AI Engineer

AI engineer building retrieval-augmented systems, previously a software developer at VDB Inc. and an R&D associate at Nokia.

Retrieval-Augmented GenerationLangChainPythonMachine learning

Related reading

Free weekly newsletter

Get the shortlist before everyone else

Every Sunday we send one email with the week’s sharpest course comparison, a career roadmap worth stealing, and the tech news and hiring signals we’re watching.

  • No fluff, ever
  • Unsubscribe anytime
  • 5-minute read

We never share your address. One click to leave.