Write lambdas for a sort key and a key function, and compare a lambda with an equivalent def.
A lambda creates a function without giving it a name. The syntax is the word lambda, then a list of parameters, then a colon, and then a single expression whose value becomes the return value.
people = [('Ada', 36), ('Grace', 45), ('Alan', 41)]
# Sort by age, and then by name.
print(sorted(people, key=lambda person: person[1]))
print(sorted(people, key=lambda person: person[0]))
# A lambda is an expression, so it can be assigned to a name.
double = lambda x: x * 2
print(double(21)) # 42
# Default values work here too, just as they do in a def.
adder = lambda a, b=10: a + b
print(adder(5)) # 15
The two sorted calls produce different orders. Sorting by age gives Ada, Alan, Grace, and sorting by name gives Alan, Grace, Ada. The lambda is exactly the function that the sort protocol asks for, because it takes one argument and gives back one value that can be compared.
Now the comparison with def. These two definitions do exactly the same job.
def double_def(x):
return x * 2
double_lambda = lambda x: x * 2
print(double_def(4), double_lambda(4)) # 8 8
They differ in three ways. A def gives the function a name that shows up in tracebacks, in help(), and in the profiler, while a lambda that is assigned to a variable does not, because the function object keeps the same anonymous placeholder name. A def can contain statements and therefore several lines of logic, while a lambda is a single expression. And a def supports a docstring, type annotations, and a comment at the end that explains what it does.
The one thing that a lambda does better is being used directly as an argument, and that is why it appears in sorted(key=...), in map, in filter, and in any callback that a library expects.