Lambdas and Small Callables
A lambda expression creates an anonymous function containing one expression:
by_last_name = lambda person: person.last_name.casefold()
people.sort(key=by_last_name)
Its best use is a short callable passed directly to an API, especially a
key= argument. A named function is clearer when logic needs a docstring, type
annotations, reuse, multiple steps, or meaningful error traces.
records.sort(key=lambda row: (row.priority, row.created_at))
Prefer comprehensions or generator expressions for ordinary transformation and filtering:
active_names = [user.name for user in users if user.active]
total = sum(order.amount for order in orders)
They usually communicate intent better than nesting map, filter, and
lambdas. map remains useful when an existing named callable already expresses
the operation.
Closure boundary
Functions close over names, whose values are looked up when the function runs. Creating callbacks in a loop therefore has a late-binding trap:
callbacks = [lambda: index for index in range(3)]
# Every callback returns 2.
Capture the current value explicitly when that is the desired contract:
callbacks = [lambda index=index: index for index in range(3)]
The default argument is evaluated while each function is created. For larger
callbacks, a small factory function or functools.partial is easier to explain.
Do not treat lambda syntax as a signal for functional purity. A lambda can call stateful functions and mutate objects just like a named function.