{"id":"functions-as-arguments-key","slug":"key-functions","title":"Sorting with a key function","text":"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.\n\nSay 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:\n\n```python\nguesses = [62, 41, 55, 30]\nprint(sorted(guesses))\n```\n\n```output\n[30, 41, 55, 62]\n```\n\nTo see who came closest, you'd rather sort the guesses by how far each one is from 50.\n\n### A key function\n\n`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:\n\n```python\ndef distance(guess):\n    return abs(guess - 50)\n\n\nguesses = [62, 41, 55, 30]\nprint(sorted(guesses, key=distance))\n```\n\n```output\n[55, 41, 62, 30]\n```\n\n`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.\n\nWith 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:\n\n```python\ndef minutes(chore):\n    return chore[1]\n\n\nchores = [(\"laundry\", 45), (\"email\", 10), (\"dishes\", 20)]\nfor chore in sorted(chores, key=minutes):\n    print(chore[0], chore[1])\n```\n\n```output\nemail 10\ndishes 20\nlaundry 45\n```\n\n### The function itself, not its result\n\nLook 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:\n\n```python\ndef distance(guess):\n    return abs(guess - 50)\n\n\nguesses = [62, 41, 55, 30]\nprint(sorted(guesses, key=distance()))\n```\n\n```crash\nTraceback (most recent call last):\n  File \"main.py\", line 6, in <module>\n    print(sorted(guesses, key=distance()))\n                              ~~~~~~~~^^\nTypeError: distance() missing 1 required positional argument: 'guess'\n```\n\nSo give `key` the function's name, without parentheses.\n\n### Ready-made keys and reverse order\n\nAny 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:\n\n```python\nwords = [\"cherry\", \"fig\", \"banana\", \"kiwi\"]\nprint(sorted(words, key=len))\nprint(sorted(words, key=len, reverse=True))\n\nnumbers = [3, 11, 7]\nnumbers.sort(reverse=True)\nprint(numbers)\n```\n\n```output\n['fig', 'kiwi', 'cherry', 'banana']\n['cherry', 'banana', 'kiwi', 'fig']\n[11, 7, 3]\n```\n\nWords of the same length, like `cherry` and `banana`, keep the order they had in the original list.\n","task":"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:\n\n```\nprinting press 1440\ntelescope 1608\ntelephone 1876\nlight bulb 1879\n```\n","kind":"fill","seed":"def year(invention):\n    return invention[1]\n\n\ninventions = [(\"telephone\", 1876), (\"printing press\", 1440), (\"light bulb\", 1879), (\"telescope\", 1608)]\n\nfor invention in sorted(inventions, key=[[year]]):\n    print(invention[0], invention[1])\n","solution":"def year(invention):\n    return invention[1]\n\n\ninventions = [(\"telephone\", 1876), (\"printing press\", 1440), (\"light bulb\", 1879), (\"telescope\", 1608)]\n\nfor invention in sorted(inventions, key=year):\n    print(invention[0], invention[1])\n","hints":["The key is the function that returns the year of one invention. Which function in the program does that?","Type only the function's name, `year`, without parentheses: `sorted` calls it itself, once for each tuple."],"tests":[{"name":"Output","stdin":"","expect":"printing press 1440\ntelescope 1608\ntelephone 1876\nlight bulb 1879","hidden":false}],"require":[],"forbid":[],"more":[{"task":"What does this program print? Type each line it prints.\n","kind":"predict","seed":"def last_letter(word):\n    return word[-1]\n\n\nwords = [\"cherry\", \"fig\", \"mango\", \"pear\"]\nprint(sorted(words, key=last_letter))\nprint(sorted(words, key=len, reverse=True))\nprint(words)\n","solution":"def last_letter(word):\n    return word[-1]\n\n\nwords = [\"cherry\", \"fig\", \"mango\", \"pear\"]\nprint(sorted(words, key=last_letter))\nprint(sorted(words, key=len, reverse=True))\nprint(words)\n","hints":["`last_letter` returns the last character of a word, so the first `sorted` orders the words by y, g, o and r.","The second one sorts by length, and `reverse=True` puts the longest word first.","`sorted` returns new lists, so `words` itself is still in its original order."],"tests":[{"name":"Output","stdin":"","expect":"['fig', 'mango', 'pear', 'cherry']\n['cherry', 'mango', 'pear', 'fig']\n['cherry', 'fig', 'mango', 'pear']","hidden":false}],"require":[],"forbid":[],"lines":["['fig', 'mango', 'pear', 'cherry']","['cherry', 'mango', 'pear', 'fig']","['cherry', 'fig', 'mango', 'pear']"]},{"task":"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,\n\n```python\nresults = [(\"Ada\", 31), (\"Ben\", 45), (\"Cleo\", 12)]\nprint(by_score(results))\nprint(results)\n```\n\nshould print:\n\n```\n[('Ben', 45), ('Ada', 31), ('Cleo', 12)]\n[('Ada', 31), ('Ben', 45), ('Cleo', 12)]\n```\n","kind":"code","seed":"def by_score(results):\n    pass\n\n\nif __name__ == \"__main__\":\n    results = [(\"Ada\", 31), (\"Ben\", 45), (\"Cleo\", 12)]\n    print(by_score(results))\n    print(results)\n","solution":"def score(result):\n    return result[1]\n\n\ndef by_score(results):\n    return sorted(results, key=score, reverse=True)\n\n\nif __name__ == \"__main__\":\n    results = [(\"Ada\", 31), (\"Ben\", 45), (\"Cleo\", 12)]\n    print(by_score(results))\n    print(results)\n","hints":["`sorted` returns a new list and leaves the original alone. `sort` would change the caller's list.","Write a key function that returns the score, the second item of a tuple: `return result[1]`.","Pass that function with `key=`, and add `reverse=True` so the highest score comes first."],"tests":[{"name":"Test 1","stdin":"","call":"results = [(\"Ada\", 31), (\"Ben\", 45), (\"Cleo\", 12)]\nprint(by_score(results))\nprint(results)\n","expect":"[('Ben', 45), ('Ada', 31), ('Cleo', 12)]\n[('Ada', 31), ('Ben', 45), ('Cleo', 12)]","hidden":false},{"name":"by_score([(\"Kim\", 7), (\"Lou\", 19), (\"Max\", 3), (\"Ned\", 23)])","stdin":"","call":"by_score([(\"Kim\", 7), (\"Lou\", 19), (\"Max\", 3), (\"Ned\", 23)])\n","expect":"[('Ned', 23), ('Lou', 19), ('Kim', 7), ('Max', 3)]","hidden":true,"returns":true},{"name":"by_score([(\"Solo\", 1)])","stdin":"","call":"by_score([(\"Solo\", 1)])\n","expect":"[('Solo', 1)]","hidden":true,"returns":true}],"require":[],"forbid":[]}]}