Python Arena
Step 1 of 4

Sorting with a key function

Python puts a list in order with the function sorted, which returns a new list, or with the method sort, which changes the list itself. Numbers go from smallest to largest, strings in alphabetical order, and tuples by their first item (and by the second item when the first ones are equal). That's their natural order, and often it's just what you need. But not always.

Say four players of a guessing game have guessed a secret number, and the number was 50. Sorting the guesses puts them from the smallest to the largest:

guesses = [62, 41, 55, 30]
print(sorted(guesses))
Output
[30, 41, 55, 62]

To see who came closest, you'd rather sort the guesses by how far each one is from 50.

A key function

sorted and sort accept a key function: a function that takes one item and returns the value to sort that item by. You write it like any other function, and hand it to sorted with key=. Here the key is a guess's distance from 50. Python's abs gives it, since abs returns a number without its minus sign:

def distance(guess):
    return abs(guess - 50)


guesses = [62, 41, 55, 30]
print(sorted(guesses, key=distance))
Output
[55, 41, 62, 30]

sorted calls distance once for each guess and puts the guesses in the order of the values it returns: 5, 9, 12, 20. The guesses themselves don't change: the new list holds 55, not 5. The key only decides their order.

With tuples, the key often picks one item of each tuple. Here each tuple holds a chore and the minutes it takes, and the key returns the minutes, so the quickest chore comes first:

def minutes(chore):
    return chore[1]


chores = [("laundry", 45), ("email", 10), ("dishes", 20)]
for chore in sorted(chores, key=minutes):
    print(chore[0], chore[1])
Output
email 10
dishes 20
laundry 45

The function itself, not its result

Look closely at key=distance: there are no parentheses after distance. A function's name on its own refers to the function, just as a variable's name refers to a list. So you can pass a function to another function like any other value, and the other function decides when to call it. With parentheses, Python would call distance right away, before sorted even starts, and with no guess to give it:

def distance(guess):
    return abs(guess - 50)


guesses = [62, 41, 55, 30]
print(sorted(guesses, key=distance()))
Python stops with
Traceback (most recent call last):
  File "main.py", line 6, in <module>
    print(sorted(guesses, key=distance()))
                              ~~~~~~~~^^
TypeError: distance() missing 1 required positional argument: 'guess'

So give key the function's name, without parentheses.

Ready-made keys and reverse order

Any function that takes one argument can be a key, Python's own functions too: key=len sorts strings by their length. And reverse=True turns the order around, with sorted and sort alike:

words = ["cherry", "fig", "banana", "kiwi"]
print(sorted(words, key=len))
print(sorted(words, key=len, reverse=True))

numbers = [3, 11, 7]
numbers.sort(reverse=True)
print(numbers)
Output
['fig', 'kiwi', 'cherry', 'banana']
['cherry', 'banana', 'kiwi', 'fig']
[11, 7, 3]

Words of the same length, like cherry and banana, keep the order they had in the original list.

Your task

Challenge 1 of 3

The program should list some inventions from the oldest to the newest. Fill in the blank so that sorted sorts the tuples by year, using the function year. The program should print:

printing press 1440
telescope 1608
telephone 1876
light bulb 1879

Challenge 2

What does this program print? Type each line it prints.

Challenge 3

Write a function by_score(results). It gets a list of tuples, each holding a player's name and score, and returns a new list sorted by score, the highest score first. The list it was given must stay as it was. For example,

results = [("Ada", 31), ("Ben", 45), ("Cleo", 12)]
print(by_score(results))
print(results)

should print:

[('Ben', 45), ('Ada', 31), ('Cleo', 12)]
[('Ada', 31), ('Ben', 45), ('Cleo', 12)]