What's the difference between `list.sort()` and `sorted()`, and how do custom sort keys work?
Quick Answer
list.sort() sorts a list in place and returns None. sorted(iterable) returns a new sorted list and works on any iterable, not just lists. Both accept a key= function (applied once per element to compute a sort key, not a full comparator) and a reverse=True flag, and both use Timsort — a stable, O(n log n) hybrid merge/insertion sort.
Detailed Answer
In-place vs new list
nums = [3, 1, 2]
nums.sort() # sorts nums in place
nums # [1, 2, 3]
result = nums.sort() # None -- sort() returns None on purpose
nums = [3, 1, 2]
new_list = sorted(nums) # returns a new sorted list
nums # [3, 1, 2] -- unchanged
sorted("cba") # ['a', 'b', 'c'] -- works on any iterable, not just lists
sort() returning None is deliberate — a Python convention where
mutating methods return None so you can't accidentally chain them and
think you're working with a new object. This is why x = lst.sort() is a
common beginner bug: x ends up None, not the sorted list.
key=: computed sort key, not a comparator
people = [{"name": "Ada", "age": 36}, {"name": "Bob", "age": 25}]
sorted(people, key=lambda p: p["age"])
# sorted by age ascending
sorted(people, key=lambda p: p["name"].lower()) # case-insensitive sort
words = ["banana", "kiwi", "apple", "fig"]
sorted(words, key=len) # sort by string length
sorted(words, key=len, reverse=True) # longest first
key is called once per element to compute a value used for
comparison. This is more efficient than an older-style comparator
function (called O(n log n) times, once per comparison), and is the only
sorting customization mechanism in modern Python (cmp= was removed in
Python 3).
Multi-key sorting via tuples
people = [{"name": "Ada", "age": 36}, {"name": "Bob", "age": 36}, {"name": "Amy", "age": 25}]
sorted(people, key=lambda p: (p["age"], p["name"]))
# sorted by age first, then name as a tiebreaker
Returning a tuple from key sorts by the first element, breaking ties
with the second, and so on — the standard idiom for multi-field sorting.
Stability matters for exactly this reason
Both sort() and sorted() use Timsort, which is stable:
elements that compare equal keep their original relative order. This is
what makes it safe to sort by one key, then sort again by another key, to
achieve a multi-level sort without needing a single combined key tuple,
though the tuple-key approach above is usually clearer for a fixed set of
sort fields.
sort() mutates in place and returns None.
sorted() returns a new list and accepts any iterable. Both use the
stable, O(n log n) Timsort algorithm and support key= (computed once per
element) for custom ordering, including multi-field sorts via tuple keys.