skip to content

sorted(), list.sort(), and key Functions

How Python sorts: sorted() returns a new list, list.sort() mutates in place, and both take a key callable run once per element. Interviewers probe stability, since sorting twice relies on it.

part ofPythonoverview, primer and where to startread it →
on this pageshow

questions

4

What is the difference between sorted() and list.sort() in Python?

level: juniorimportance: must knowfreq 85%

answer

  1. One returns, one mutates
  2. Which one works on a set?
  3. Watch the return value of the method
  4. Python's convention for in-place mutators
  5. Extra list of n pointers versus none

basics

~20 s

sorted() takes any iterable and returns a new sorted list, leaving the original untouched. list.sort() reorders an existing list in place and returns None. Both run the same stable sort and accept the same key and reverse arguments.

solid answer

~40 s

`sorted(iterable)` builds and returns a **new list**; the input can be any iterable — a set, a generator, a dict view, a string — and the original is never modified. `list.sort()` is a method on `list` only: it reorders the list **in place** and returns `None`, following Python's convention that mutating methods return `None` (like `list.append` or `random.shuffle`). Both use the same stable sort and both take keyword-only `key` and `reverse` arguments, so the choice is about ownership and memory, not ordering: use `list.sort()` when you own the list and want to avoid allocating a second one, and `sorted()` when the input is not a list, when the caller's data must stay intact, or when you need a sorted value inside an expression. The classic beginner bug is `ranked = items.sort()`, which binds `None`.

code

pycon · 9 lines
pycon
>>> nums = [3, 1, 2]
>>> sorted(nums)
[1, 2, 3]
>>> nums
[3, 1, 2]
>>> print(nums.sort())
None
>>> nums
[1, 2, 3]

go deeper

for a junior

Recall the one-line distinction and say it without hedging: new list versus in-place, and the method returns None. Be able to spot top = items.sort() as a bug in a code snippet on sight.

for a middle

Explain the mechanics behind it — the in-place-mutators-return-None convention shared with append and shuffle, the fact that sorted() accepts any iterable but always yields a list, and the extra n-pointer allocation sorted() pays.

for a senior

Show the API-design judgment: sorting a caller's list in place mutates shared state, so library code should return a new list unless it documents otherwise. Mention CPython raising ValueError if the list is modified during a sort.

for a principal

Own the convention question — when your codebase's own helpers should mutate versus return, and how consistently following Python's return-None-on-mutation rule keeps aliasing bugs out of shared data structures across teams.

## Two entry points, one sorting algorithm Python exposes its sort twice. `sorted(iterable, /, *, key=None, reverse=False)` is a builtin function that consumes **any iterable** and returns a brand-new `list`. `list.sort(*, key=None, reverse=False)` is a method that exists only on `list` objects; it rearranges the elements of that list **in place** and returns `None`. Underneath, they are the same machinery: `sorted()` is essentially "copy the iterable into a list, then call that list's `sort`". So neither is faster in comparison count, neither is more stable, and any behaviour you learn about one applies to the other. ## Why `list.sort()` returns None This surprises newcomers, and interviewers ask about it because the answer reveals whether a candidate understands a language-wide convention: **in-place mutators in Python return `None`**. `list.append`, `list.extend`, `list.reverse`, `dict.update`, `set.add` and `random.shuffle` all do it. The convention is deliberate — it stops you writing `data.sort().pop()`, a chain that reads like it produced a new value when it actually mutated the object you already had. The cost is the single most common sorting bug in Python: ```python ranked = scores.sort() # ranked is None; scores was mutated for s in scores.sort(): # TypeError: 'NoneType' object is not iterable ``` When you want a sorted copy, `sorted(scores)` is the answer, not `scores.sort()`. ## What each one accepts and returns `sorted()` accepts anything iterable and always returns a `list`, regardless of the input type: ```python sorted({3, 1, 2}) # [1, 2, 3] from a set sorted({"b": 1, "a": 2}) # ['a', 'b'] iterates the keys sorted("banana") # ['a','a','a','b','n','n'] a list, not a str sorted(x * x for x in r) # consumes a generator ``` That last row is a frequent gotcha: sorting a string gives you a list of characters, so you need `"".join(sorted(text))` to get a string back. `list.sort()` has no such flexibility — a set or a generator has no `.sort()` method, so you must materialize a list first, at which point you may as well have called `sorted()`. ## Memory and mutation The practical difference is one extra list of `n` pointers. On a list of ten million items in a memory-tight process, `data.sort()` reorders the existing array while `sorted(data)` transiently holds both the old and the new list. When you are the owner of the data and nobody else holds a reference to it, in-place sorting is the cheaper call. Mutation is the flip side. If a function receives a list from a caller and sorts it in place, it has silently changed the caller's object — a real source of bugs in shared-state code. A library function that must not surprise its caller should return `sorted(items)` rather than mutate the argument, or document loudly that it reorders in place. ## Shared behaviour worth naming Both take `key` and `reverse` as **keyword-only** arguments, so `sorted(words, len)` is a `TypeError` — it must be `sorted(words, key=len)`. Both are **stable**: elements that compare equal keep their relative input order. And in CPython both refuse to be re-entered — while a sort is running the list is temporarily made to look empty, so a `key` function that peeks at the list being sorted sees `[]`, and mutating that list mid-sort raises `ValueError: list modified during sort`. That safeguard exists precisely because in-place sorting hands the algorithm an object user code can still reach. Finally, do not confuse `sorted(data, reverse=True)` with `reversed(data)`. The first sorts and then presents the order from largest to smallest; the second does no sorting at all, it just walks the existing sequence backwards and returns a lazy iterator. ## Choosing in review Use `list.sort()` when you own a list, the copy would be wasteful, and the statement stands alone. Use `sorted()` when the source is not a list, when the original order still matters to somebody, or when you need the result as an expression — inside a comprehension, a `return`, or an f-string. Reaching for `sorted()` by default and switching to in-place sorting when profiling says the copy hurts is a defensible habit.

  • Why did the language designers make list.sort() return None instead of the sorted list?
    It is a deliberate, language-wide convention: methods that mutate in place return `None` — `list.append`, `list.reverse`, `dict.update`, `random.shuffle` all do the same. Returning the list would invite chains like `data.sort().pop()` that read as if a new object were produced when the original was actually modified. The convention makes mutation visible at the call site, at the price of the familiar `x = items.sort()` beginner bug.
  • When would you prefer list.sort() over sorted() on a very large list?
    When you own the list and memory is tight. `sorted()` allocates a second list of `n` pointers before sorting, so a ten-million-element list transiently costs roughly double. `list.sort()` reorders the existing array. The tradeoff is that in-place sorting mutates an object the caller may still be holding, so it is the right call for data you created locally and the wrong one for an argument handed to you by someone else.
  • What is the difference between sorted(data, reverse=True) and reversed(data)?
    `sorted(data, reverse=True)` actually sorts, then presents the result from largest to smallest, and returns a new list. `reversed(data)` performs no ordering at all — it returns a lazy iterator that walks the existing sequence back to front, so on unsorted input it just yields the same elements in the opposite arrival order. They coincide only when the input is already sorted ascending.

sorted() is photocopying a stack of pages into a tidy new pile; list.sort() is reshuffling the original stack on the desk and handing you back nothing.

saying these in an interview costs you the question

  • Says list.sort() returns the sorted list
  • Assigns the result of list.sort() to a variable
  • Claims sorted() is slower because it uses a different algorithm
  • Calls sorted() on a set and expects a set back
  • Thinks list.sort() works on any iterable such as a generator
  • Confuses reversed() with sorting in descending order

context

open as a page

What does sorted()'s key argument receive, and how many times is it called?

level: middleimportance: must knowfreq 70%

basics

~20 s

key is a one-argument callable applied to each element exactly once, before any comparing happens; the sort then orders elements by those computed key values. For n elements it runs n times, not once per comparison.

open as a page

Python's sorted() is stable, so why do tied records still swap order between runs of a 340-case regression pack?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Stability preserves the order equal keys arrived in, so it is only as deterministic as the input. A set or a directory listing hands the sort a different order each run. Fix it with a total key that leaves no ties.

open as a page

When do you need functools.cmp_to_key instead of a plain key function for sorting?

level: middleimportance: nice to knowfreq 22%

basics

~20 s

Only when the ordering rule is genuinely pairwise and cannot be expressed as a value computed from one element alone, or when porting a legacy two-argument comparator. functools.cmp_to_key wraps that comparator so sorted() can use it, at a real performance cost.

open as a page