Lambda Functions and Higher-Order Functions

Lambda functions are anonymous, single-expression functions defined inline. They are most useful when you need a simple function to pass as an argument โ€” to sorted(), map(), filter(), or a FastAPI response hook โ€” without the ceremony of a full def statement. Higher-order functions take other functions as arguments or return functions, enabling powerful data transformation patterns. Python’s built-in map(), filter(), and sorted() all accept functions as arguments and are commonly used in FastAPI for shaping query results and validating data collections.

Lambda Functions

# Syntax: lambda parameters: expression
# A lambda is a function โ€” it just has no name and no return statement

# Named function
def square(n):
    return n ** 2

# Equivalent lambda
square_lambda = lambda n: n ** 2

print(square(5))          # 25
print(square_lambda(5))   # 25

# Multiple parameters
add = lambda a, b: a + b
print(add(3, 4))   # 7

# Lambda with default
greet = lambda name="World": f"Hello, {name}!"
print(greet())          # Hello, World!
print(greet("Alice"))   # Hello, Alice!

# Immediately invoked (rare but valid)
result = (lambda x, y: x * y)(3, 4)
print(result)   # 12
Note: Lambdas are limited to a single expression โ€” no statements, no assignments, no multiple lines, no return keyword. If your logic requires an if/else, it must be a ternary expression: lambda x: "even" if x % 2 == 0 else "odd". If you need anything more complex โ€” local variables, multiple statements, or exception handling โ€” use a named def function. PEP 8 (Python’s style guide) actually discourages assigning a lambda to a variable; use def for that case.
Tip: The primary use of lambda in Python is as an inline argument to functions that accept callables โ€” particularly as the key argument to sorted(), min(), and max(). For example: sorted(posts, key=lambda p: p["created_at"], reverse=True) sorts posts by creation date in descending order without defining a separate function. This pattern is extremely common when shaping FastAPI query results.
Warning: Do not use lambda when a named function would be clearer. The Python community prefers named functions for anything beyond a trivial key or comparison. If you find yourself writing a complex lambda, it is a sign you should use def instead. Code reviewers will flag complex lambdas as unreadable. Remember: code is read far more often than it is written โ€” optimise for the reader, not the writer.

sorted() with Key Functions

posts = [
    {"id": 3, "title": "C Post", "views": 150, "created": "2025-03-01"},
    {"id": 1, "title": "A Post", "views": 500, "created": "2025-01-01"},
    {"id": 2, "title": "B Post", "views": 75,  "created": "2025-02-01"},
]

# Sort by a specific field
by_title   = sorted(posts, key=lambda p: p["title"])
by_views   = sorted(posts, key=lambda p: p["views"], reverse=True)   # descending
by_created = sorted(posts, key=lambda p: p["created"])

# Multiple sort criteria (sort by views desc, then title asc)
by_multi = sorted(posts, key=lambda p: (-p["views"], p["title"]))

# Sort strings case-insensitively
names = ["Charlie", "alice", "Bob"]
sorted(names, key=str.lower)   # ["alice", "Bob", "Charlie"]
# str.lower is a method reference โ€” equivalent to lambda s: s.lower()

# min() and max() with key
most_popular = max(posts, key=lambda p: p["views"])
print(most_popular["title"])   # A Post

map() and filter()

numbers = [1, 2, 3, 4, 5, 6]

# map(function, iterable) โ€” applies function to every item
# Returns a map object (lazy) โ€” convert to list if needed
squares  = list(map(lambda n: n ** 2, numbers))
# [1, 4, 9, 16, 25, 36]

# Same with list comprehension (usually preferred)
squares2 = [n ** 2 for n in numbers]   # โ† more Pythonic

# filter(function, iterable) โ€” keeps items where function returns True
evens  = list(filter(lambda n: n % 2 == 0, numbers))
# [2, 4, 6]

# Same with list comprehension (usually preferred)
evens2 = [n for n in numbers if n % 2 == 0]   # โ† more Pythonic

# map with a named function
def format_price(price):
    return f"${price:.2f}"

prices    = [19.99, 5.50, 100.0]
formatted = list(map(format_price, prices))
# ["$19.99", "$5.50", "$100.00"]

# FastAPI use: transform DB rows to response format
def row_to_response(row):
    return {"id": row.id, "title": row.title, "slug": row.slug}

response_data = list(map(row_to_response, db_rows))

Functions as Arguments โ€” Higher-Order Functions

# A higher-order function takes a function as an argument or returns one

# Built-in higher-order functions: sorted, map, filter, max, min, any, all

# any() โ€” True if at least one item is truthy
has_admin = any(u["role"] == "admin" for u in users)

# all() โ€” True only if ALL items are truthy
all_published = all(p["published"] for p in posts)

# Custom higher-order function
def apply_to_all(items, func):
    return [func(item) for item in items]

def double(n): return n * 2

apply_to_all([1, 2, 3], double)          # [2, 4, 6]
apply_to_all(["a", "b"], str.upper)      # ["A", "B"]
apply_to_all([1, 2, 3], lambda n: n**3)  # [1, 8, 27]

# Function factories (return a function)
def make_validator(min_len, max_len):
    def validate(text):
        return min_len <= len(text) <= max_len
    return validate

validate_username = make_validator(3, 20)
validate_bio      = make_validator(0, 500)

print(validate_username("Al"))        # False (too short)
print(validate_username("Alice"))     # True
print(validate_bio("Hello world"))    # True

Common Mistakes

Mistake 1 โ€” Using lambda when a named function is clearer

โŒ Wrong โ€” complex lambda is hard to read:

result = sorted(posts, key=lambda p: (p["published"], -p["views"], p["title"].lower()))

โœ… Correct โ€” named function with a docstring explains intent:

def sort_key(post):
    return (post["published"], -post["views"], post["title"].lower())

result = sorted(posts, key=sort_key)   # โœ“ readable

Mistake 2 โ€” Forgetting map() and filter() return lazy objects

โŒ Wrong โ€” comparing lazy object to a list:

result = map(lambda n: n*2, [1, 2, 3])
print(result == [2, 4, 6])   # False โ€” result is a map object, not a list

โœ… Correct โ€” convert to list first:

result = list(map(lambda n: n*2, [1, 2, 3]))
print(result == [2, 4, 6])   # True โœ“

Mistake 3 โ€” Using lambda instead of operator module

โŒ Wrong โ€” lambda wrapping a simple operator:

from functools import reduce
product = reduce(lambda a, b: a * b, [1, 2, 3, 4])   # works but verbose

โœ… Correct โ€” use operator module for common operations:

import operator
product = reduce(operator.mul, [1, 2, 3, 4])   # โœ“ cleaner

Quick Reference

Pattern Code
Simple lambda lambda x: x * 2
Lambda with ternary lambda x: "yes" if x else "no"
Sort by field sorted(items, key=lambda x: x["field"])
Sort descending sorted(items, key=lambda x: x["n"], reverse=True)
Transform all items list(map(func, items))
Filter items list(filter(func, items))
Any truthy any(cond for item in items)
All truthy all(cond for item in items)

🧠 Test Yourself

You have a list of user dicts. How do you sort them by last_name ascending, then by first_name ascending as a tiebreaker, in one expression?