Higher-order functions and lambdas

INPUT · Slides

Hand over what to sort by

01 / 07

Left alone, it does not sort how you want

You met sorted in chapter 2 — hand it a list and a new, sorted list comes back.

But the moment you want them shortest first, that is not enough on its own. sorted puts text in alphabetical order.

names = ["Haruto", "Yui", "Minato"]print(sorted(names))

Result

['Haruto', 'Minato', 'Yui']

02 / 07

Tell it what to sort by with key

sorted has a named argument called key. Hand a function to it and everything is sorted by what that function gives back.

For length that is len. Write key=len and nothing else, and you get shortest first.

names = ["Haruto", "Yui", "Minato"]print(sorted(names, key=len))

Result

['Yui', 'Haruto', 'Minato']

03 / 07

There are no parentheses on len

Here is the thing that matters most. It is key=len, not key=len().

  • len() … calls the function. You would be handing over the result of the call
  • len … hands over the function itself

What sorted wants is a way of counting letters, not an answer about the length of something in particular. It is saying lend me the tool as it is; I will call it myself later.

Put the parentheses on and you get the error below.

names = ["Haruto", "Yui", "Minato"]print(sorted(names, key=len()))

Result

TypeError: len() takes exactly one argument (0 given)

04 / 07

A one-off rule goes in a lambda

There is not always a ready-made function that fits, the way len does.

Say you want them youngest first. You want a function that takes one person and returns their age, and no such thing exists.

This is where the lambda from the last lesson earns its keep. Make it on the spot and hand it over on the spot. Far too small to be worth a name — which is precisely what lambda is for.

key=lambda p: p["age"] is "a function that takes one person p and returns p["age"]". Sort by this number, in other words.

people = [    {"name": "Yui", "age": 20},    {"name": "Haruto", "age": 15},    {"name": "Minato", "age": 18},]for person in sorted(people, key=lambda p: p["age"]):    print(person["name"])

Result

Haruto
Minato
Yui

05 / 07

Together with reverse

reverse=True, from chapter 2, works alongside key. Just list them with a comma.

For oldest first, pull the age out with key and turn it round with reverse=True. One decides what to sort by and the other decides which way; the jobs are separate.

people = [    {"name": "Yui", "age": 20},    {"name": "Haruto", "age": 15},    {"name": "Minato", "age": 18},]for person in sorted(people, key=lambda p: p["age"], reverse=True):    print(person["name"])

Result

Yui
Minato
Haruto

06 / 07

sort takes it the same way

sort, which sorts the list you already have, takes key in exactly the same way.

How you choose between them is unchanged from chapter 2. sorted if you want to keep the original, sort if you are happy for it to be sorted in place. Adding key does not affect that judgement.

names = ["Haruto", "Yui", "Minato"]names.sort(key=len)print(names)

Result

['Yui', 'Haruto', 'Minato']

07 / 07

What is actually happening

Inside, sorted is calling the function you gave it on every item one at a time. With key=len it calls len("Yui"), len("Haruto") and so on for all of them, and sorts by the numbers that come back.

So sorted is leaving part of the sorting to whoever calls it. A function that takes a function like this is called a higher-order function.

Next lesson, see the same key handed to max and min. "Find the biggest one", which took five lines in chapter 2, becomes one.