Python Sorting
Beginner Data Structures

Python Sorting

1Python Sorting: overview

Sorting puts items in order: sorted() returns a new sorted list and leaves the original alone, list.sort() rearranges the list in place and returns None, and the key= function decides what Python compares.

GoalBy the end you can sort with sorted() and .sort(), reverse an order, sort by any rule with key=, order tuples and dictionaries, sort on several fields at once, and pick the best item with min() or max().

Before this lesson: Python Lists, Python Functions

2Python Sorting: diagram

sorted(words, key=len)sorted(words, key=len)original itemkey = len(item)sorted position"pear"42nd"fig"31st"banana"64th"kiwi"43rdSorted by key, but the original items come back. Equal keys keep their order.
The key function is called once per item. Python ranks the items by those key values, but the result still contains the original items, and items with equal keys stay in their original order.

3Python Sorting: explained step by step

Sorting means putting items in order: scores from high to low, names from A to Z, tasks from most to least urgent. Python gives you two tools for it, and the difference between them causes more confusion than the sorting itself.

sorted() builds a new list

sorted() accepts any collection and returns a new list with the items in order. The original is not touched, so it is safe to use anywhere.

Python
scores = [72, 95, 88, 61]
ranked = sorted(scores)
print(ranked)
print(scores)

Because it accepts any iterable, sorted() also works on tuples, sets, strings and dictionary keys. The result is always a list: sorted("python") gives a list of single letters, not a string.

list.sort() changes the list itself

my_list.sort() rearranges the list in place and returns None. It does not build a second list, but only lists have it and the old order is gone for good.

Python
scores = [72, 95, 88, 61]
result = scores.sort()
print(scores)
print(result)

A good rule: use .sort() on a list you own and no longer need in the old order, and use sorted() when you must keep the original or when the data is not a list. Never write scores = scores.sort(), because that replaces your list with None.

Descending order, and why reversed() is different

Pass reverse=True to either tool to get the largest value first. Do not confuse it with reversed(), which does no comparing at all: it just walks the items backwards from whatever order they are already in, and gives you an iterator to wrap in list().

Python
scores = [72, 95, 88, 61]
print(sorted(scores, reverse=True))
print(list(reversed(scores)))

Strings: capital letters and numbers written as text

Text is compared one character at a time using each character's code. Every capital letter has a smaller code than every lowercase letter, so "Zoe" comes before "adam". Digits are characters too, so "10" comes before "9": Python sees that "1" is smaller than "9" and stops there.

Python
names = ["maya", "Zoe", "adam", "Ben"]
print(sorted(names))
print(sorted(names, key=str.lower))

ids = ["10", "9", "100", "2"]
print(sorted(ids))
print(sorted(ids, key=int))

Both fixes use the same tool, key=.

key=: choose what to compare

key takes a function. Python calls it once for every item, sorts by the values it returns, and then hands back the original items. The diagram above shows key=len: the words are ranked by their lengths, but you still get words.

Python
words = ["pear", "fig", "banana", "kiwi"]
print(sorted(words, key=len))
print(sorted(words, key=lambda w: w[-1]))

A lambda is a tiny function written on one line: lambda w: w[-1] means "given w, return its last letter". A normal def function works in key= just as well. Always pass the function itself (key=len), never a call (key=len()).

Sorting tuples, records and dictionaries

Tuples are compared by their first item, then by the second when the first ties. To sort by a different position, pass a key. operator.itemgetter builds a key function that picks out one position or one dictionary key, and it is shorter than a lambda.

Python
from operator import itemgetter

orders = [("tea", 3.5), ("cake", 4.0), ("coffee", 2.5)]
print(sorted(orders))
print(sorted(orders, key=itemgetter(1)))

students = [
    {"name": "Asha", "score": 82},
    {"name": "Ben", "score": 91},
    {"name": "Cara", "score": 77},
]
best_first = sorted(students, key=itemgetter("score"), reverse=True)
for student in best_first:
    print(student["name"], student["score"])

The students are dictionaries, records that store named fields as key: value pairs (the dictionary lessons come next). For objects, operator.attrgetter("score") does the same job for attributes.

A dictionary cannot be sorted in place, but you can sort its pairs and build a new dictionary, which remembers that order:

Python
from operator import itemgetter

stock = {"apples": 12, "pears": 0, "plums": 7}
print(sorted(stock))
by_count = dict(sorted(stock.items(), key=itemgetter(1), reverse=True))
print(by_count)

sorted(stock) on its own sorts the keys only; stock.items() gives you the (key, value) pairs to rank.

Sorting by several things at once

When the key function returns a tuple, Python compares the first part, and only looks at the next part if the first ones tie. A minus sign flips a number, so one part can run high to low while the other runs A to Z:

Python
players = [("Ben", 90), ("Asha", 90), ("Cara", 75), ("Dev", 82)]
print(sorted(players, key=lambda p: (-p[1], p[0])))

Highest score first, and players on the same score in alphabetical order. The minus trick only works for numbers.

Stability

Python's sort is stable: items with equal keys keep the order they had before. That makes ties predictable, and it gives you a way to mix directions on text. Sort by the less important rule first, then by the main rule:

Python
players = [("Ben", 90), ("Asha", 90), ("Cara", 75), ("Dev", 82)]
step1 = sorted(players, key=lambda p: p[0], reverse=True)
step2 = sorted(step1, key=lambda p: p[1])
print(step2)

Step 1 puts the names in Z to A order. Step 2 sorts by score, low to high, and because it is stable the two 90s stay in Z to A order.

min() and max() take key too

If you only need the best or the worst item, don't sort the whole list. min() and max() accept the same key=, and default= covers an empty collection:

Python
from operator import itemgetter

cart = {"pen": 1.5, "lamp": 24.0, "mug": 6.0}
print(max(cart, key=cart.get))
print(min(cart.items(), key=itemgetter(1)))
print(max(["kiwi", "banana", "fig"], key=len))
print(max([], default="empty"))

Items must be comparable

Sorting works by asking "is this item smaller than that one?". Numbers and text cannot answer that question for each other, so mixing them raises a TypeError. None is just as stubborn. Clean the data first, or use a key that turns every item into the same type.

Python
mixed = [3, "2", 1]
try:
    sorted(mixed)
except TypeError as err:
    print(err)
print(sorted(mixed, key=int))

Quick reference: Python Sorting

4Python Sorting: syntax

new_list = sorted(items)                 # new list, original unchanged
sorted(items, reverse=True)              # largest first
my_list.sort()                           # in place, returns None
sorted(items, key=len)                   # sort by a rule
sorted(items, key=str.lower)             # case-insensitive
sorted(rows, key=lambda r: (-r[1], r[0]))  # several rules at once
from operator import itemgetter, attrgetter
sorted(rows, key=itemgetter("score"))      # sort records by a field
sorted(d.items(), key=itemgetter(1))     # sort a dict's pairs by value
max(items, key=len, default=None)        # best item by a rule
list(reversed(items))                    # just flip the order

5Python Sorting: run the example

Press Run (or Ctrl + Enter). The output (step 6) appears directly below the code. Change anything and run it again.

Interactive example
players = [
    {"name": "Maya", "score": 88},
    {"name": "Ben", "score": 95},
    {"name": "Asha", "score": 88},
    {"name": "Dev", "score": 71},
]

ranking = sorted(players, key=lambda p: (-p["score"], p["name"]))
for place, player in enumerate(ranking, start=1):
    print(place, player["name"], player["score"])

best = max(players, key=lambda p: p["score"])
worst = min(players, key=lambda p: p["score"])
print("Best:", best["name"], "| Lowest:", worst["name"])
print("Original first:", players[0]["name"])
Expected output1 Ben 95
2 Asha 88
3 Maya 88
4 Dev 71
Best: Ben | Lowest: Dev
Original first: Maya

7Python Sorting: how the example works

Hover or tap a step to highlight its lines in the example above.

  1. Line 1-6players is a list of four dictionaries. Each player is one record with a "name" and a "score".
  2. Line 8sorted() builds a new list. The key returns the tuple (-score, name) for every player: the minus sign puts the highest score first, and the name breaks ties from A to Z. That is why Asha comes before Maya, who both scored 88.
  3. Line 9-10enumerate(..., start=1) hands out the place numbers 1, 2, 3, 4 while the loop walks through the sorted list and prints each player.
  4. Line 12-13max() and min() use the same key= idea: they compare the scores but return the whole dictionary of the best and the worst player, not the score itself.
  5. Line 14max() returned a whole dictionary, so best["name"] reads the name from it. Ben has the highest score and Dev the lowest.
  6. Line 15players still starts with Maya. sorted() made a new list, so the original order was never touched.

8Python Sorting: common mistakes

Assigning the result of .sort()

✗ Wrong

scores = [72, 95, 88]
scores = scores.sort()
print(scores[0])
TypeError: 'NoneType' object is not subscriptable

✓ Right

scores = [72, 95, 88]
scores.sort()
print(scores[0])

.sort() changes the list in place and returns None, so scores now holds None and the list is lost. Call .sort() on its own line, or use sorted() if you want a new list.

Sorting numbers that are stored as text

✗ Wrong

ids = ["10", "9", "100"]
print(sorted(ids))
No error, but it prints ['10', '100', '9']: text is compared character by character

✓ Right

ids = ["10", "9", "100"]
print(sorted(ids, key=int))

Strings are compared one character at a time, so "10" is smaller than "9". Tell Python to compare the numeric value with key=int; you still get the original strings back.

Mixing numbers and text in one list

✗ Wrong

readings = [18, "21", 19]
print(sorted(readings))
TypeError: '<' not supported between instances of 'str' and 'int'

✓ Right

readings = [18, "21", 19]
numbers = []
for item in readings:
    numbers.append(int(item))
print(sorted(numbers))

Python cannot decide whether "21" is smaller than 18. Convert every item to the same type before sorting. (The error may name the two types in the opposite order, depending on which pair Python compared first.)

Calling the key function instead of passing it

✗ Wrong

words = ["pear", "fig", "banana"]
print(sorted(words, key=len()))
TypeError: len() takes exactly one argument (0 given)

✓ Right

words = ["pear", "fig", "banana"]
print(sorted(words, key=len))

key needs the function itself, so Python can call it once per item. Writing len() calls it right now, with no argument. Drop the parentheses.

Sorting a dictionary and expecting pairs or values

✗ Wrong

stock = {"apples": 12, "pears": 0, "plums": 7}
print(sorted(stock))
No error, but only the keys come back: ['apples', 'pears', 'plums']

✓ Right

stock = {"apples": 12, "pears": 0, "plums": 7}
print(sorted(stock.items(), key=lambda pair: pair[1]))

Looping over a dictionary (and so sorting it) gives its keys. To rank by value, sort stock.items() and tell key to look at the second part of each pair.

9Python Sorting: exercises

Write your answer, then press Check. The checks run in your browser.

1. Leaderboard without changing the scores

Write code Easy

Complete the function leaderboard(scores). It must return a new list with the scores from highest to lowest, and it must not change the list it receives (the caller may still need the original order).

Your code
def leaderboard(scores):
    # return a NEW list, highest score first
    pass

results = [72, 95, 88, 61]
print(leaderboard(results))
print(results)

2. Predict the three orders

Predict the output Easy

Read the code and type exactly what it prints, line by line. Then press Check to run it and compare.

Read this code
words = ["banana", "apple", "Cherry", "Apple"]
print(sorted(words))
print(sorted(words, key=str.lower))
print(max(words, key=len))

2 more exercises → 6 more examples

Finished this lesson?
Progress is saved in this browser — create a free account to keep it across devices.

Questions learners ask

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

sorted(items) works on any iterable and returns a new sorted list, leaving the original unchanged. my_list.sort() exists only on lists, sorts the list in place and returns None. Both accept key= and reverse=.

How do I sort a list of dictionaries by one of their keys?

Pass a key function that reads that field: sorted(rows, key=lambda r: r["score"]), or the shorter sorted(rows, key=itemgetter("score")) from the operator module. Add reverse=True for largest first, or return a tuple from the key to sort by several fields.

Why does Python sort "10" before "9" and capital letters before lowercase ones?

Strings are compared one character at a time by character code. "1" is smaller than "9", so "10" comes first, and every capital letter has a smaller code than every lowercase letter. Use key=int for numbers stored as text and key=str.lower (or str.casefold) for case-insensitive order.

Further reading

10Next lesson

Up nextPython SetsA set is an unordered collection of unique values: adding a value that is already there changes nothing, and checking w…

Related:
Python ListsPython List MethodsPython DictionariesPython Lambda Functions

← Python Unpacking · All Data Structures lessons · Practise in the IDE