Course Content
Python Essentials for AI Engineer
6 sections · 48 lessons
What is a lambda function?
What you need to know
Syntax
1square = lambda x: x ** 2 # works, but PEP 8 says use def for named functions2def square_def(x): return x ** 23print(square(4), square_def(4)) # 16 1645label = lambda p: "fraud" if p > 0.8 else "ok" # a conditional EXPRESSION is allowed6print(label(0.93), label(0.2)) # fraud oklambda params: expression — no return keyword, no statements like if blocks, for loops, try or assignments. The value of the expression is returned.
Where lambdas fit
Their natural home is as a key function. sorted, min, max and many pandas methods call it once per item to get the value to compare:
1results = [2 {"doc": "refund-policy", "score": 0.82},3 {"doc": "kyc-guide", "score": 0.47},4 {"doc": "upi-limits", "score": 0.91},5]6top2 = sorted(results, key=lambda r: r["score"], reverse=True)[:2]7print([r["doc"] for r in top2]) # ['upi-limits', 'refund-policy']For simple field access, operator.itemgetter("score") does the same job.
map and filter vs comprehensions
list(map(lambda x: x * 2, nums)) works, but [x * 2 for x in nums] is shorter and clearer. Most Python style guides prefer comprehensions.
The late-binding trap
1funcs = [lambda: i for i in range(3)]2print([f() for f in funcs]) # [2, 2, 2], not [0, 1, 2]3funcs = [lambda i=i: i for i in range(3)]4print([f() for f in funcs]) # [0, 1, 2]A lambda looks up i when it is called, not when it is created, so all three see the final value. Binding it as a default argument captures the value at creation time. This applies to def inside loops too.
A real-life example
You benchmark three LLMs on 200 support tickets and need to choose one:
1models = [2 {"name": "model-a", "accuracy": 0.91, "p95_ms": 1800, "cost_per_1k": 0.60},3 {"name": "model-b", "accuracy": 0.89, "p95_ms": 650, "cost_per_1k": 0.15},4 {"name": "model-c", "accuracy": 0.84, "p95_ms": 400, "cost_per_1k": 0.05},5]6fast_enough = [m for m in models if m["p95_ms"] <= 1000]7best = max(fast_enough, key=lambda m: m["accuracy"])8print(best["name"]) # model-b9cheapest = min(models, key=lambda m: (m["cost_per_1k"], -m["accuracy"]))10print(cheapest["name"]) # model-cEach lambda is a one-line rule used once, so a named function would add noise. If the selection rule grew into a weighted score with budget checks, it would become a def score_model(m) with a docstring and a unit test.
Follow-up questions to expect
- "Can a lambda have multiple statements?" — No. One expression only. A conditional expression (
a if cond else b) is allowed. - "Is a lambda faster than a
def?" — No. Both compile to the same kind of function object; the difference is only syntax. - "What is the late-binding problem?" — Lambdas created in a loop all see the loop variable's final value; fix it with a default argument like
lambda i=i: ....