Python max() and min() with key=: Choose the Best Item
Python's max() and min() with key= pick the best item by a rule you choose, replacing a best-candidate loop with one call.
Read Tracking the Best Candidate first. You should know how a loop keeps the best item so far, and why it starts from None when the list may be empty.
This lesson replaces the loop with one call, then shows a shorter loop for when you still need one. Every block uses fixed data, and the text under it says what it prints.
Pick the longest with max(key=len)
key= takes a function. max() calls it on every item and keeps the item with the largest result. It returns the item, not the result.
names = ["Bo", "Maya", "Alex"]
print(max(names, key=len))
print(min(names, key=len))
print(max(names))
This prints Maya, Bo, then Maya. On a tie, max() and min() keep the first item, like a loop that uses >. Without key=, strings compare alphabetically, so max(names) is Maya for a different reason.
Closest to a target with your own key
Write a small function that returns the value to compare. Pass its name, without brackets.
target = 10
def distance(number):
return abs(number - target)
print(min([14, 8, 12], key=distance))
This prints 8. The distances are 4, 2 and 2, and 8 comes before 12. To keep the last tied item instead, you need the loop with <=; key= has no option for that.
Empty lists and default=
An empty list has no best item. max() and min() raise ValueError, unless you give default=.
try:
print(max([], key=len))
except ValueError:
print("max() needs at least one item")
print(max([], key=len, default=None))
This prints the error message, then None. default=None matches the loop that starts from None.
Choose between the loop and max()
Use max() or min() when "best" is one value per item and ties keep the first. Keep the loop when you also need the score, a custom tie rule, or several things updated together. For example, max(scores, key=scores.get) gives the top name in a dictionary, but not the top score.
scores = {"Mia": 9, "Leo": 12, "Zara": 12}
top = max(scores, key=scores.get)
print(top, scores[top])
This prints Leo 12. Looping over a dictionary gives its keys, and scores.get turns each key into its score.
Shorten the loop with or
When you keep the loop, it needs one branch for "no candidate yet" and one for "better candidate". or joins them into one condition.
longest = None
for name in ["Bo", "Maya", "Alex"]:
if longest is None or len(name) > len(longest):
longest = name
print(longest)
This prints Maya. Python reads or left to right and short-circuits: when longest is None is true, it skips the right side. That is why len(None) never runs. Swap the two sides and the first pass raises TypeError.
Note: this lesson leaves out lambda, which writes a small key= function inline. See Lambda Functions.
See also: sum(), max() and min().
Your turn
Store the name with the lowest score in bottom, then print it with its score. Ties keep the first name. Then print the lowest name in empty, or None when it has no names.
scores = {"Mia": 9, "Leo": 12, "Zara": 7, "Omar": 7}
empty = {}
# add three lines: bottom = min() with key=, print bottom and its score,
# then print min() on empty with key= and default=None
Expected output
Zara 7
None