Lambda Functions
A lambda is a small anonymous function written as a single expression. It exists for the places where naming a function would add nothing.
- Basics
- Data Types
- Operators
- Strings
- Control Flow
- Lists
- Tuples
- Sets
- Dictionaries
- Comprehensions
- Functions
- Advanced Functions
- Recursion
- Exception Handling
- File Handling
- Modules
- Standard Library
- OOP
- Advanced OOP
- Iterators and Generators
- Decorators
- Context Managers
- Descriptors and Dataclasses
- Python Internals
- Concurrency
- Regular Expressions
- Serialization
- Command Line Python
- Testing and Debugging
- Type Hints
- Performance
- Python Security
- DSA with Python
The syntax
lambda parameters: expressiondouble = lambda n: n * 2
print(double(5)) # 10
# The same thing with def
def double(n):
return n * 2- No
def, no name, noreturn. - The body is a single expression, and its value is returned automatically.
- It may take any number of parameters, including none.
add = lambda a, b: a + b
greet = lambda name="there": f"Hello, {name}"
constant = lambda: 42
print(add(2, 3), greet(), greet("Meera"), constant())Assigning a lambda to a name, as above, is legal and is discouraged by PEP 8. If it deserves a name, it deserves adef- which also gives it a proper__name__for tracebacks. The examples above exist to show the equivalence, not the recommended style.
Where lambdas belong
A lambda earns its place as an argument to a function that expects a small function.
Sorting with a key
people = [
{"name": "Meera", "age": 27},
{"name": "Arun", "age": 31},
{"name": "Sara", "age": 24},
]
print(sorted(people, key=lambda p: p["age"]))
print(sorted(people, key=lambda p: p["name"]))
print(sorted(people, key=lambda p: (-p["age"], p["name"])))
words = ["banana", "kiwi", "apple"]
print(sorted(words, key=lambda w: len(w)))
print(sorted(words, key=lambda w: w[-1]))
pairs = [(1, "b"), (3, "a"), (2, "c")]
print(sorted(pairs, key=lambda pair: pair[1]))max and min with a key
print(max(people, key=lambda p: p["age"])["name"]) # Arun
print(min(words, key=len)) # kiwi
scores = {"Meera": 92, "Arun": 78}
print(max(scores, key=lambda name: scores[name])) # Meera
print(max(scores, key=scores.get)) # the same, no lambdamap and filter
numbers = [1, 2, 3, 4, 5, 6]
print(list(map(lambda n: n * n, numbers))) # [1, 4, 9, 16, 25, 36]
print(list(filter(lambda n: n % 2 == 0, numbers))) # [2, 4, 6]
# A comprehension is usually clearer
print([n * n for n in numbers])
print([n for n in numbers if n % 2 == 0])In Python, a comprehension normally beats map or filter with a lambda. Reach for map when the function already exists and needs no wrapper: map(str, numbers) or map(str.upper, words).
Grouping and reducing
from functools import reduce
numbers = [1, 2, 3, 4]
print(reduce(lambda a, b: a * b, numbers)) # 24
print(reduce(lambda a, b: a + b, numbers, 100)) # 110, with a starting valuesum, max and min cover most cases without reduce. Use reduce for genuinely custom accumulation, such as a product.
Default behaviour in a dispatch table
operations = {
"add": lambda a, b: a + b,
"subtract": lambda a, b: a - b,
"multiply": lambda a, b: a * b,
"power": lambda a, b: a ** b,
}
print(operations["multiply"](6, 7)) # 42
for name in sorted(operations):
print(f"{name:<10}{operations[name](8, 2)}")This is one of the few places a stored lambda reads better than a def: the whole table is visible in one block.
What a lambda cannot do
# No statements
# f = lambda x: print(x); return x # SyntaxError
# f = lambda x: if x > 0: "pos" # SyntaxError
# f = lambda x: for i in range(x): ... # SyntaxError
# A conditional EXPRESSION is fine, because it is an expression
classify = lambda n: "positive" if n > 0 else "non-positive"
print(classify(5), classify(-5))
# No assignment, except with the walrus operator
# f = lambda x: y = x * 2 # SyntaxErrorNo if statements, no loops, no try, no assignments, no multiple lines, no docstring, no annotations. If you need any of those, you need def.
The late binding trap
functions = []
for i in range(3):
functions.append(lambda: i)
print([f() for f in functions]) # [2, 2, 2] - not [0, 1, 2]The lambda does not capture the value of i; it captures the variable. By the time the functions run, the loop has finished and i is 2. Bind the value with a default argument:
functions = [lambda i=i: i for i in range(3)]
print([f() for f in functions]) # [0, 1, 2]This applies to every closure, not only lambdas. The advanced functions note covers the mechanism.
Lambda or def?
| Use a lambda | Use def |
|---|---|
| One short expression | Anything needing a statement |
| Passed directly as an argument | Called from more than one place |
| Its purpose is obvious in context | It needs a name, a docstring or tests |
A key= function | Longer than about forty characters |
# Fine
sorted(records, key=lambda r: r["date"])
# Not fine - give it a name
sorted(records, key=lambda r: (r["dept"], -r["salary"], r["name"].lower()))
def ranking_key(record):
"""Sort by department, then salary descending, then name."""
return record["dept"], -record["salary"], record["name"].lower()
sorted(records, key=ranking_key)Alternatives worth knowing
import operator
pairs = [(1, "b"), (3, "a"), (2, "c")]
print(sorted(pairs, key=operator.itemgetter(1))) # instead of lambda p: p[1]
people = [{"name": "Meera", "age": 27}]
print(sorted(people, key=operator.itemgetter("age")))
from functools import partial
def power(base, exponent):
return base ** exponent
square = partial(power, exponent=2)
print(square(7)) # 49Common mistakes
- Trying to put a statement inside a lambda.
- Assigning every lambda to a name instead of using
def. - Writing a lambda so long it needs to be read twice.
- Falling into the late binding trap in a loop.
- Using
mapandfilterwith a lambda where a comprehension is clearer. - Forgetting that
mapandfilterreturn lazy objects, not lists.
Best practices
- Use a lambda only as an argument, and keep it to one short expression.
- Name it with
defthe moment it needs explaining. - Prefer comprehensions to
mapandfilterwith lambdas. - Use
operator.itemgetterandattrgetterfor plain field access. - Bind loop values with a default argument when creating functions in a loop.
Practice
- Sort a list of employee records by department ascending and salary descending, first with a lambda and then with a named function. Say which you prefer.
- Build a calculator dispatch table of four lambdas and drive it from user input.
- Demonstrate the late binding trap and fix it two different ways.
- Rewrite
list(map(lambda x: x.strip().lower(), items))as a comprehension. - Explain why
lambda x: x = 5is a syntax error.
Conclusion
A lambda is one expression, passed somewhere, used immediately. It shines as a key= function and in small dispatch tables. Everywhere else - and always once it needs a name, a docstring or a second line - use def.