Python – Lambda Functions

July 22, 20265 min readUpdated 8/20/2026

A lambda is a function written as an expression, with no name. It exists for one situation: when you need to hand a small piece of behaviour to something else and giving it a name would be noise.

The syntax

double = lambda n: n * 2

def double_def(n):
    return n * 2

print(double(21))         # Output: 42
print(double_def(21))     # Output: 42

lambda, the parameters, a colon, and one expression whose value is returned. There is no return keyword because there is nothing else it could do.

Those two are the same function. Which is the point of the next section: if you are assigning a lambda to a name, you have written def the long way round.

Do not assign one to a name

double = lambda n: n * 2 is legal and you should not write it. PEP 8 says so explicitly, and the reason is practical rather than stylistic — a named def knows its own name and a lambda does not:

double = lambda n: n * 2

def double_def(n):
    return n * 2

print(double.__name__)        # Output: <lambda>
print(double_def.__name__)    # Output: double_def

That name is what appears in a traceback. Fill a module with lambdas assigned to names and every stack trace says <lambda>, which is precisely the moment you needed it to say something else. You also give up docstrings, default arguments read badly, and you cannot put more than one statement in it anyway.

Where it does belong: a sort key

This is the case that justifies the whole feature.

accounts = [
    {"type": "Savings", "balance": 8400.50},
    {"type": "Checking", "balance": 1250.00},
    {"type": "Credit", "balance": -320.75},
]

for account in sorted(accounts, key=lambda a: a["balance"]):
    print(f"{account['type']:<10}{account['balance']:>10,.2f}")

# Output: Credit       -320.75
# Output: Checking    1,250.00
# Output: Savings     8,400.50

sorted() calls the key function once per item and sorts by what comes back. Defining def balance_of(a): return a["balance"] three lines above, using it once, and never referring to it again would be strictly more to read.

Sort by several things at once by returning a tuple, and negate a number to reverse just that part:

accounts = [
    {"type": "Checking", "balance": 90.00},
    {"type": "Checking", "balance": 1250.00},
    {"type": "Savings", "balance": 8400.50},
]

ordered = sorted(accounts, key=lambda a: (a["type"], -a["balance"]))
for account in ordered:
    print(f"{account['type']} {account['balance']:,.2f}")

# Output: Checking 1,250.00
# Output: Checking 90.00
# Output: Savings 8,400.50

In real code

The console bank app that supplies this track's examples uses lambdas exactly this way — as the test handed to a lookup, and as a sort key:

def find_by_account(self, account_id: int, limit: int) -> list[Transaction]:
    """The statement for one account, newest first, at most `limit` lines."""
    rows = self.find(lambda t: t.account_id == account_id)
    rows.sort(key=lambda t: t.timestamp, reverse=True)
    return rows[:limit]

Two lambdas, both one expression, neither given a name. find() takes any function of one argument that returns a bool; the lambda says which transactions this call wants. That is the shape to look for.

Other places you will pass one

amounts = [100, -25, 250, -40]

print(max(amounts, key=lambda n: abs(n)))          # Output: 250
print(sorted(amounts, key=abs))                    # Output: [-25, -40, 100, 250]
print(list(filter(lambda n: n > 0, amounts)))      # Output: [100, 250]
print([n for n in amounts if n > 0])               # Output: [100, 250]

Two things worth noticing. key=abs on the second line beats key=lambda n: abs(n) — if the lambda does nothing but call one function, pass that function directly. And the last two lines do the same job, which brings us to the rule below.

filter and map: prefer the comprehension

amounts = [100, -25, 250, -40]

print(list(map(lambda n: n * 2, amounts)))         # Output: [200, -50, 500, -80]
print([n * 2 for n in amounts])                    # Output: [200, -50, 500, -80]

print(list(filter(lambda n: n > 0, amounts)))      # Output: [100, 250]
print([n for n in amounts if n > 0])               # Output: [100, 250]

map and filter exist and work. The comprehension is shorter, does not need the list() wrapper to be printable, and is what Python programmers read fastest. If you are writing map or filter with a lambda, the comprehension is almost certainly the better line.

Where map still wins is when you already have a named function and no lambda is needed at all — map(str.strip, lines) is clean.

What will not fit in one

A lambda holds one expression, so anything that is a statement is out: no assignment, no if block, no for, no try, no raise. A conditional expression is allowed, because it is an expression:

amounts = [100, -25, 250]

label = lambda n: "credit" if n > 0 else "debit"
print([label(n) for n in amounts])     # Output: ['credit', 'debit', 'credit']

That works, and it is also the point at which a def is the better call — it has a name worth having and somewhere to put a docstring.

The constraint is a feature. When a lambda stops fitting, the language is telling you the logic has outgrown being an anonymous argument.

The loop variable trap

A lambda captures the variable, not its value at the time:

multipliers = []
for n in [1, 2, 3]:
    multipliers.append(lambda x: x * n)

assert [m(10) for m in multipliers] == [10, 20, 30], "all three captured the same n"

That assertion fails — every lambda returns 30, because all three closed over the same n, which finished the loop holding 3. Bind the value with a default argument, which is evaluated at definition time:

multipliers = []
for n in [1, 2, 3]:
    multipliers.append(lambda x, n=n: x * n)

print([m(10) for m in multipliers])     # Output: [10, 20, 30]

Same rule as the mutable default in Functions, seen from the other side: defaults are evaluated once, at definition, and here that is what saves you.

The rule

Use a lambda when it is an argument to another function and fits comfortably on that line. Anything else — anything you name, anything needing a second line, anything you would want to test — is a def.

Next: Generators & Iterators.