Python Lambda Expressions — Anonymous Functions: Practice Problems & Exercises
Practice: Lambda Expressions — Anonymous Functions at Engineering Depth
← Back to lessonEasy
Write a single lambda expression assigned to square that returns the square of its argument. Call it with 7.
square = lambda x: x * x print(square(7))
Solution
square = lambda x: x * x
print(square(7)) # 49
Explanation: lambda x: x * x creates an anonymous function object. Assigning it to square gives it a name, but the function's internal __name__ attribute remains '<lambda>'.
# Create a lambda that squares a number, then call it with 7
square = None # replace with a lambda
print(square(7))Expected Output
49Hints
Hint 1: A lambda takes the form: lambda x: expression.
Hint 2: The expression should return x multiplied by itself.
Use sorted() with a lambda key to sort the list words by string length, shortest first.
words = ['banana', 'fig', 'apple', 'kiwi', 'strawberry'] result = sorted(words, key=lambda w: len(w)) print(result)
Solution
words = ['banana', 'fig', 'apple', 'kiwi', 'strawberry']
result = sorted(words, key=lambda w: len(w))
print(result) # ['fig', 'kiwi', 'apple', 'banana', 'strawberry']
Explanation: sorted() calls the key function once per element and sorts by the returned values. lambda w: len(w) is equivalent to str.__len__ — you could also write key=len directly, but the lambda form illustrates the pattern.
words = ['banana', 'fig', 'apple', 'kiwi', 'strawberry']
# Sort words by length (shortest first) using a lambda key
result = sorted(words, key=None) # fix the key
print(result)Expected Output
['fig', 'kiwi', 'apple', 'banana', 'strawberry']Hints
Hint 1: Pass a lambda as the key= argument to sorted().
Hint 2: The lambda should receive a word and return its length.
Use filter() with a lambda to extract all even numbers from numbers.
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] evens = list(filter(lambda n: n % 2 == 0, numbers)) print(evens)
Solution
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = list(filter(lambda n: n % 2 == 0, numbers))
print(evens) # [2, 4, 6, 8, 10]
Explanation: filter() returns a lazy iterator that yields each element for which the predicate returns a truthy value. Wrapping in list() materialises it.
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Use filter() + a lambda to keep only even numbers
evens = list(filter(None, numbers)) # fix the first argument
print(evens)Expected Output
[2, 4, 6, 8, 10]Hints
Hint 1: filter(func, iterable) keeps elements where func(element) is truthy.
Hint 2: A number is even when number % 2 == 0.
Sort the list of (number, fruit) tuples alphabetically by the fruit name (second element).
pairs = [(1, 'banana'), (3, 'apple'), (2, 'cherry')] result = sorted(pairs, key=lambda t: t[1]) print(result)
Solution
pairs = [(1, 'banana'), (3, 'apple'), (2, 'cherry')]
result = sorted(pairs, key=lambda t: t[1])
print(result) # [(3, 'apple'), (1, 'banana'), (2, 'cherry')]
Explanation: The lambda extracts the second element of each tuple. sorted() uses those string values for comparison, leaving the tuples intact in the output.
pairs = [(1, 'banana'), (3, 'apple'), (2, 'cherry')]
# Sort by the string (second element) alphabetically
result = sorted(pairs, key=None) # fix the key
print(result)Expected Output
[(3, 'apple'), (1, 'banana'), (2, 'cherry')]Hints
Hint 1: Index into the tuple with t[1] to get the second element.
Hint 2: Lambda: lambda t: t[1]
Medium
Sort students by grade descending, then by name ascending when grades are tied. Return a single sorted() call using a lambda key.
students = [
('Alice', 85),
('Bob', 92),
('Charlie', 85),
('Diana', 92),
]
result = sorted(students, key=lambda s: (-s[1], s[0]))
print(result)Solution
students = [
('Alice', 85),
('Bob', 92),
('Charlie', 85),
('Diana', 92),
]
result = sorted(students, key=lambda s: (-s[1], s[0]))
print(result)
# [('Bob', 92), ('Diana', 92), ('Alice', 85), ('Charlie', 85)]
Explanation: The lambda returns (-grade, name). Negating the grade makes higher grades sort first (since -92 < -85). When grades are equal, the second tuple element name breaks the tie alphabetically.
students = [
('Alice', 85),
('Bob', 92),
('Charlie', 85),
('Diana', 92),
]
# Sort: highest grade first, then alphabetically by name within same grade
result = sorted(students, key=None) # fix the key
print(result)Expected Output
[('Bob', 92), ('Diana', 92), ('Alice', 85), ('Charlie', 85)]Hints
Hint 1: Return a tuple from the lambda — Python compares tuples element by element.
Hint 2: Negate the grade to sort it descending: -grade.
Hint 3: The name part sorts ascending (alphabetically) naturally.
Write an immediately-invoked lambda expression (IIFE) that receives the list [1, 2, 3, 4, 5] and returns the sum of its squares. No helper variables — one expression.
result = (lambda nums: sum(x * x for x in nums))([1, 2, 3, 4, 5]) print(result)
Solution
result = (lambda nums: sum(x * x for x in nums))([1, 2, 3, 4, 5])
print(result) # 55
Explanation: (lambda nums: ...)([1,2,3,4,5]) creates the lambda object and calls it in the same expression. The body uses a generator expression inside sum() — no intermediate list is allocated.
# Use an immediately-invoked lambda to compute the
# sum of squares of [1, 2, 3, 4, 5] — no separate variable assignment.
# Expected result: 55
result = None # replace with an IIFE lambda expression
print(result)Expected Output
55Hints
Hint 1: Wrap the lambda in parentheses and call it immediately: (lambda ...: ...)(...)
Hint 2: sum() with a generator expression can compute sum of squares.
Hint 3: Pass the list directly as the argument to the lambda.
The list comprehension below creates five lambdas that are all broken due to late binding — they all use i=4. Fix the lambda so each captures its own i at creation time.
# Broken version funcs_broken = [lambda x: x + i for i in range(5)] print([f(0) for f in funcs_broken]) # [4, 4, 4, 4, 4] # Fixed version funcs_fixed = [lambda x, i=i: x + i for i in range(5)] print([f(0) for f in funcs_fixed]) # [0, 1, 2, 3, 4]
Solution
funcs = [lambda x, i=i: x + i for i in range(5)]
print([f(0) for f in funcs]) # [0, 1, 2, 3, 4]
Explanation: Without i=i, every lambda's body references the name i in the enclosing scope. After the loop, i is 4, so all lambdas return x + 4. Adding i=i as a default parameter evaluates the right-hand i immediately at definition time, binding the current value into the parameter default — this is the canonical Python fix.
# This code has the classic loop-closure (late-binding) bug.
# Fix it so each lambda captures the value of i at creation time.
funcs = [lambda x: x + i for i in range(5)]
# Currently all funcs use i=4 (the final loop value)
print([f(0) for f in funcs]) # Should print [0, 1, 2, 3, 4]Expected Output
[0, 1, 2, 3, 4]Hints
Hint 1: Python lambdas close over the variable i, not its value at creation.
Hint 2: Force early binding by adding a default argument: lambda x, i=i: x + i
Hint 3: The default argument is evaluated at definition time, capturing the current value.
Implement compose(f, g) that returns a new function computing f(g(x)). Use a lambda inside the body.
def compose(f, g):
return lambda x: f(g(x))
double = lambda x: x * 2
add_ten = lambda x: x + 10
double_then_add = compose(double, add_ten)
add_then_double = compose(add_ten, double)
print(double_then_add(5)) # 30
print(add_then_double(5)) # 20Solution
def compose(f, g):
return lambda x: f(g(x))
double = lambda x: x * 2
add_ten = lambda x: x + 10
print(compose(double, add_ten)(5)) # 30
print(compose(add_ten, double)(5)) # 20
Explanation: The returned lambda closes over f and g. When called with x, it applies g first, then passes the result to f. This is standard mathematical function composition: (f ∘ g)(x) = f(g(x)).
def compose(f, g):
# Return a new function that computes f(g(x))
pass
double = lambda x: x * 2
add_ten = lambda x: x + 10
double_then_add = compose(double, add_ten)
add_then_double = compose(add_ten, double)
print(double_then_add(5)) # double(add_ten(5)) = double(15) = 30
print(add_then_double(5)) # add_ten(double(5)) = add_ten(10) = 20Expected Output
30\n20Hints
Hint 1: compose(f, g) should return a callable that accepts x.
Hint 2: Use a lambda inside compose: return lambda x: f(g(x))
Hint 3: Note the order: g runs first, f runs on the result.
Hard
Create a dispatch table ops as a plain dict where every value is a lambda. The 'div' entry must raise ValueError("division by zero") when b == 0. Implement run() to look up and call the right lambda.
def _safe_div(a, b):
if b == 0:
raise ValueError("division by zero")
return a / b
ops = {
'add': lambda a, b: a + b,
'sub': lambda a, b: a - b,
'mul': lambda a, b: a * b,
'div': lambda a, b: _safe_div(a, b),
}
def run(op_name, a, b):
if op_name not in ops:
raise KeyError(f"Unknown op: {op_name}")
return ops[op_name](a, b)
print(run('add', 10, 3))
print(run('sub', 10, 3))
print(run('mul', 10, 3))
print(run('div', 10, 2))Solution
def _safe_div(a, b):
if b == 0:
raise ValueError("division by zero")
return a / b
ops = {
'add': lambda a, b: a + b,
'sub': lambda a, b: a - b,
'mul': lambda a, b: a * b,
'div': lambda a, b: _safe_div(a, b),
}
def run(op_name, a, b):
if op_name not in ops:
raise KeyError(f"Unknown op: {op_name}")
return ops[op_name](a, b)
print(run('add', 10, 3)) # 13
print(run('sub', 10, 3)) # 7
print(run('mul', 10, 3)) # 30
print(run('div', 10, 2)) # 5.0
Explanation: Lambdas cannot contain statements (like if/raise), so the error-checking logic lives in a helper _safe_div. The lambda for 'div' delegates to it. This pattern — a dispatch table of lambdas/functions indexed by string keys — replaces large if/elif chains and is O(1) lookup.
# Build a mini command dispatcher using a dict of lambdas.
# Supported commands: 'add', 'sub', 'mul', 'div'
# Each maps to a lambda(a, b) that performs the operation.
# 'div' should raise ValueError if b == 0.
ops = {
# fill in the four lambdas
}
def run(op_name, a, b):
if op_name not in ops:
raise KeyError(f"Unknown op: {op_name}")
return ops[op_name](a, b)
print(run('add', 10, 3)) # 13
print(run('sub', 10, 3)) # 7
print(run('mul', 10, 3)) # 30
print(run('div', 10, 2)) # 5.0Expected Output
13\n7\n30\n5.0Hints
Hint 1: Each dict value is a lambda that accepts two arguments.
Hint 2: For 'div', you need to check the second argument inside the lambda body — but lambdas can only hold an expression, not a statement.
Hint 3: Use a ternary expression: value_if_true if condition else raise_alternative — or call a helper.
Hint 4: One clean approach: wrap the division in a small def, or raise inside a nested function call.
Write a memoized Fibonacci lambda in a single expression (no def, no functools.lru_cache). The memoisation dict must be bound at definition time using the default-argument trick.
fib = lambda n, cache={0: 0, 1: 1}: (
cache[n] if n in cache
else cache.setdefault(n, fib(n - 1) + fib(n - 2))
)
print(fib(0)) # 0
print(fib(1)) # 1
print(fib(10)) # 55
print(fib(30)) # 832040Solution
fib = lambda n, cache={0: 0, 1: 1}: (
cache[n] if n in cache
else cache.setdefault(n, fib(n - 1) + fib(n - 2))
)
print(fib(10)) # 55
print(fib(30)) # 832040
Explanation: The default argument cache={} is a mutable dict created once at definition time and shared across every call (the classic mutable-default-argument pattern, used intentionally here). dict.setdefault(key, value) inserts and returns the value only if the key is absent — this is the only way to do a conditional assignment in a lambda expression. The lambda calls itself by name fib, which works because fib is defined in the enclosing scope by the time any recursive call runs.
# Implement a one-liner memoized Fibonacci using only lambdas and a dict.
# The cache dict must be created once and shared across all calls.
# Hint: use the default-argument trick to bind the dict at definition time.
fib = None # replace with a lambda (recursive is fine here)
print(fib(0)) # 0
print(fib(1)) # 1
print(fib(10)) # 55
print(fib(30)) # 832040Expected Output
0\n1\n55\n832040Hints
Hint 1: Use the default-mutable-argument trick: lambda n, cache={}: ...
Hint 2: Inside the lambda body, check if n is in cache; if so return cache[n].
Hint 3: You cannot use assignment statements inside a lambda — use dict.setdefault() or the walrus operator.
Hint 4: Recursion: the lambda calls itself by name (fib) — make sure fib is assigned before calling fib(10).
Implement pipeline(funcs, value) using functools.reduce and a lambda. It should apply each function in funcs left-to-right, threading the result through each step.
from functools import reduce
def pipeline(funcs, value):
return reduce(lambda acc, f: f(acc), funcs, value)
steps = [
lambda x: x * 2,
lambda x: x + 100,
lambda x: str(x),
lambda x: x + '!',
]
print(pipeline(steps, 10)) # 120!
print(pipeline(steps, 50)) # 200!Solution
from functools import reduce
def pipeline(funcs, value):
return reduce(lambda acc, f: f(acc), funcs, value)
steps = [
lambda x: x * 2,
lambda x: x + 100,
lambda x: str(x),
lambda x: x + '!',
]
print(pipeline(steps, 10)) # 120!
print(pipeline(steps, 50)) # 200!
Explanation: reduce(lambda acc, f: f(acc), funcs, value) starts with acc = value, then for each function f in funcs it sets acc = f(acc). This is the functional "left fold" pattern — the accumulator carries the intermediate result and each step transforms it. The pattern is the backbone of data transformation pipelines in functional programming.
from functools import reduce
# Build a pipeline that applies a list of lambdas in sequence to a value.
# pipeline([f1, f2, f3], x) should return f3(f2(f1(x))).
def pipeline(funcs, value):
pass
steps = [
lambda x: x * 2, # step 1: double
lambda x: x + 100, # step 2: add 100
lambda x: str(x), # step 3: convert to string
lambda x: x + '!', # step 4: append exclamation
]
print(pipeline(steps, 10)) # '120!'
print(pipeline(steps, 50)) # '200!'Expected Output
120!\n200!Hints
Hint 1: reduce(func, iterable, initializer) folds a sequence into a single value.
Hint 2: Think of it as: start with value, apply f1, feed result to f2, feed result to f3, ...
Hint 3: The reducing function takes the accumulated value and the next function, then applies it.
Hint 4: reduce(lambda acc, f: f(acc), funcs, value)
