What's the difference between `list.sort()` and `sorted()`, and how do custom sort keys work?

5 minbeginnercollectionssorting

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.