Skip to main content

Lambdas & higher-order functions

Level 2: Level 2: Idiomsmedium11 minlambdashigher-order-functionssortedmap

Pass functions as values: sorted(key=...), map, and filter.

Why passing functions is worth learning

Half of real Python data code is "sort these records by the right field," "transform every row," or "keep the rows that match." You could write a loop each time, but the standard library already has fast, tested tools for this: sorted, map, and filter. The catch is that they need you to hand them a function that describes the rule. Learn to pass a function as an argument and a page of loops collapses into one clear line.

Functions are values

In Python a function is an ordinary value, like an int or a list. You can store it in a variable, put it in a list, and pass it into another function to call later. A function that takes or returns a function is a higher-order function. sorted, map, and filter are all higher-order: they do the looping, you supply the rule.

Check yourself
What does list(filter(lambda x: x * 2, [0, 1, 2, 3])) return?
Table
All three take a rule and do the looping, and the highlighted column is what distinguishes them: sorted wants a key, map wants a replacement, filter wants a verdict. Mixing them up is why filter(lambda x: x * 2, xs) silently keeps everything, since every non-zero number is truthy.
ToolWhat your function returnsWhat comes backLength of the result
sorted(xs, key=f)a sort key for one itema new listsame as the input
map(f, xs)the replacement for one itema lazy iteratorsame as the input
filter(f, xs)True to keep, False to dropa lazy iteratorsame or shorter
All three take a rule and do the looping, and the highlighted column is what distinguishes them: sorted wants a key, map wants a replacement, filter wants a verdict. Mixing them up is why filter(lambda x: x * 2, xs) silently keeps everything, since every non-zero number is truthy.

Lambdas: a rule with no name

A lambda is a one-expression function you write inline, without a def and without a name:

square = lambda x: x * x
square(5)   # 25

lambda x: x * x is the same idea as def square(x): return x * x, just shorter and anonymous. You will rarely assign one to a variable (use def for that). Lambdas exist to be passed straight into a higher-order function.

sorted with a key

sorted returns a new sorted list and never changes the original. Its key argument is a function applied to each element to decide what to sort by:

words = ["ccc", "a", "bb"]
sorted(words, key=len)              # ['a', 'bb', 'ccc']  (shortest first)
sorted(words, key=lambda w: w[-1])  # sort by last character

You can pass a built-in like len directly, or write a lambda for a custom rule. Add reverse=True to sort largest-first without touching your key, so sorted(words, key=len, reverse=True) puts the longest word first. This is exactly what the Apply asks for.

map and filter

map applies a function to every item; filter keeps the items where the function returns a truthy value. Both return lazy iterators, so wrap them in list(...) to get a real list:

list(map(lambda w: w.upper(), words))     # ['CCC', 'A', 'BB']
list(filter(lambda x: x % 2 == 0, nums))  # keep even numbers
Check yourself
words = ['ccc', 'a', 'bb']. You write result = words.sort() and then print(result). What prints?

Pitfalls

  • sort versus sorted. sorted(x) returns a new list; x.sort() sorts in place and returns None. Writing result = words.sort() gives you None, a bug interns hit constantly. In the Apply, return sorted(words, key=len), not words.sort().
  • Iterators are one-shot. A map or filter object is exhausted after you walk it once. m = map(...); list(m) works, but a second list(m) returns []. Call list(...) once and keep that list.
Check yourself
You need employees ordered by department name ascending, and within each department by salary descending. Using two calls to sorted, which order do you run them in?

Interview nuance: Python's sorted is stable and computes each key exactly once per element (the decorate-sort-undecorate strategy). Stable means elements with equal keys stay in their original relative order, which lets you sort by a secondary field first and a primary field second to build a multi-key sort. Computing key once means n key calls plus O(n log n) comparisons on those cheap precomputed keys, so an expensive key is evaluated n times, not on every comparison.

Worked example (Python)
words = ["ccc", "a", "bb"]
print(sorted(words, key=len))                 # ['a', 'bb', 'ccc']
print(list(map(lambda w: w.upper(), words)))  # ['CCC', 'A', 'BB']

Apply

Your turn

The task this lesson builds to.

Implement sort_by_length(words): return words sorted from shortest to longest.

For ["ccc", "a", "bb"] return ["a", "bb", "ccc"]. Pass a key to sorted.

3 hints and 4 automated checks are waiting in the workspace.

Practice

Make it stick

A second problem on the same idea, so it survives past today.

Implement shout_all(words): return a new list where each word is uppercased with a "!" appended.

For ["hi", "go"] return ["HI!", "GO!"]. Use map with a lambda.

3 hints and 4 automated checks are waiting in the workspace.