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: mutating
methods return None so you can't accidentally chain them and think
you're working with a new object), which 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.
Interview-ready summary: 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.