max() returns the largest item from its arguments or from an iterable: max(3, 7, 2) is 7, and max([3, 7, 2]) is 7. That much is obvious. The part worth learning is the key argument, which lets you define what largest means - the longest string, the newest record, the item with the highest score - without writing a loop. max(words, key=len) gives you the longest word. Once key clicks, max() replaces a surprising amount of hand-rolled comparison code.
Table of contents
- The two ways to call max
- The key argument: defining what largest means
- Avoiding the empty-iterable crash with default
- Ties, and how max resolves them
- max versus a manual loop
- How this fits the rest of the stack
- FAQ
The two ways to call max
max() has two forms, and mixing them up is the first thing that trips people:
max(3, 7, 2) # several arguments -> 7
max([3, 7, 2]) # one iterable -> 7
max("hello") # one iterable of chars -> 'o'
Pass two or more positional arguments and it compares them. Pass a single iterable and it compares the items inside it. What you cannot do is pass a single number - max(5) raises TypeError, because a number is not iterable.
On an empty iterable, max([]) raises ValueError. That is a real trap in code that filters a list and then takes the max, because the filter can legitimately return nothing. The fix is the default argument, covered below.
The key argument: defining what largest means
By default max compares items directly. key lets you compare a derived value instead. You pass a function; max calls it on each item and compares the results, but returns the original item.
words = ["pip", "python", "venv", "wheel"]
max(words) # 'wheel' - alphabetical, the default
max(words, key=len) # 'python' - the longest
key=len means compare by length, return the actual word. This generalizes to anything:
servers = [
{"name": "api", "load": 0.42},
{"name": "worker","load": 0.91},
{"name": "cache", "load": 0.13},
]
busiest = max(servers, key=lambda s: s["load"])
# {'name': 'worker', 'load': 0.91}
Any time you find yourself writing a loop that tracks a current best and updates it, max with a key is the shorter, clearer version. It reads as a statement of intent rather than a mechanism.
Avoiding the empty-iterable crash with default
max on an empty sequence raises ValueError: max() arg is an empty sequence. In real code the sequence is often the result of a filter or comprehension, so it can be empty for perfectly ordinary reasons.
scores = [s for s in results if s > 0]
max(scores) # ValueError if nothing was positive
max(scores, default=0) # returns 0 instead of raising
default is only valid when you pass a single iterable, and it is the clean way to say when there is nothing, use this. It beats wrapping the call in a try/except, and it beats a length check followed by a branch. Reach for it whenever the input can be legitimately empty.
Ties, and how max resolves them
When two items are equal under the key, max returns the first one it encountered. That is a documented, stable rule, not luck:
data = [("a", 3), ("b", 3), ("c", 1)]
max(data, key=lambda t: t[1]) # ('a', 3) - first of the tied pair
If you need the last of the tied group instead, reverse the iterable or negate a secondary key. And if you want the item that is largest on one field but breaks ties on another, return a tuple from key - tuples compare element by element:
max(data, key=lambda t: (t[1], t[0])) # highest number, then latest letter
Returning a tuple from key is the trick that turns max into a full multi-field ranking in one line. It is the same idea that powers sorted(..., key=...).
max versus a manual loop
There is nothing wrong with a loop, but compare the two:
# manual
best = servers[0]
for s in servers[1:]:
if s["load"] > best["load"]:
best = s
# with max
best = max(servers, key=lambda s: s["load"])
The max version cannot get the initial value wrong, cannot skip an element, and states the goal in the first word. The loop has three places to introduce an off-by-one or a bad seed. For finding a single extreme, max and min are the right tools, and key is what makes them apply to real objects rather than just bare numbers.
How this fits the rest of the stack
Small, correct building blocks like max with a key are the difference between code that reads clearly and code you have to trace line by line. The same instinct - let the platform express intent instead of hand-rolling the mechanism - is what a good deployment layer gives you once that Python is running as a real service. The RunxBuild hosting calculator lays out the service, database, storage, and bandwidth as separate line items, and the RunxBuild dashboard is where the team watches deploys, logs, and restarts as they happen.
Useful related references:
- Python Append to List: append, extend, and the Default-Argument Trap
- Begin OpenSSH Private Key: Format, Generation, and Conversion
- Change Python Version: The Three Tools That Make It Stop Hurting
- Python services on RunxBuild
FAQ
What does max do in Python?
max() returns the largest item. Called with several arguments it compares them - max(3, 7, 2) is 7; called with one iterable it compares the items inside - max([3, 7, 2]) is 7. For strings it compares alphabetically unless you pass a key.
How does the key argument work in max?
key is a function applied to each item; max compares the results but returns the original item. max(words, key=len) returns the longest word, and max(records, key=lambda r: r['score']) returns the record with the highest score without you writing a loop.
How do I stop max from crashing on an empty list?
Pass default: max(scores, default=0) returns 0 instead of raising ValueError when the iterable is empty. default is only allowed when you call max with a single iterable, and it is cleaner than a try/except or a length check.
What does max return when there is a tie?
It returns the first item that achieves the maximum under the key. If you need the last of a tied group, reverse the input or add a secondary key by returning a tuple from key, since tuples compare element by element.
Can max compare objects, not just numbers?
Yes, through key. Return whatever field or derived value you want to rank by - a length, an attribute, a dictionary value, or a tuple for multi-field ranking - and max returns the original object that scored highest.