Learning Goals
3 minBy the end of this lesson you can:
- Choose between
list.sort()andsorted(list)and say why. - Sort largest-first with
reverse=True. - Sort by your own rule with
key=, such as length or a chosen field.
Warm-Up · The Sort That Returned Nothing
5 minA very common first attempt. What does it print?
marks = [78, 91, 65] result = marks.sort() print(result) print(marks)
Answer
Output
None [65, 78, 91]
sort() rearranged marks and returned None. The sorted list is in the variable you already had — assigning the result throws it away.
Lesson 2: lists change in place. sort() follows that rule and hands back nothing. marks = marks.sort() replaces your list with None and loses the data entirely.
New Concept · Two Sorts, One Difference
12 minTidying a bookshelf versus writing out a tidy catalogue. One rearranges the shelf; the other leaves the shelf alone and produces a new list.
marks = [78, 91, 65, 88] marks.sort() print("sorted in place:", marks) names = ["Ana", "Mia", "Leo"] alphabetical = sorted(names) print("new list: ", alphabetical) print("original:", names)
Output
sorted in place: [65, 78, 88, 91] new list: ['Mia', 'Ana', 'Leo'] original: ['Ana', 'Mia', 'Leo']
items.sort()— a method, changes the list, returnsNone. Use when the order you had does not matter.sorted(items)— a built-in, leaves the list alone, returns a new one. Use when you need both orders.
Backwards
marks = [78, 91, 65, 88] print(sorted(marks, reverse=True)) print(sorted(["Ana", "Mia", "Leo"], reverse=True))
Output
[91, 88, 78, 65] ['Leo', 'Ana', 'Mia']
reverse=True works on both sort() and sorted(). It is not the same as items.reverse(), which flips the current order without sorting at all.
Sorting by your own rule
key= takes a function, and Python calls it on each item to decide what to compare. Note the missing brackets: you pass the function itself, not a call to it.
dishes = ["ice cream", "pizza", "hot dog", "pancake telur"] print(sorted(dishes)) print(sorted(dishes, key=len)) print(sorted(dishes, key=len, reverse=True))
Output
['ice cream', 'pizza', 'pancake telur', 'hot dog'] ['hot dog', 'ice cream', 'pizza', 'pancake telur'] ['pancake telur', 'pizza', 'ice cream', 'hot dog']
The first is alphabetical; the others are by length, short-first then long-first. Same list, three orders, one argument.
sorted(["banana", "Apple"]) gives ['Apple', 'banana'], because every capital letter comes before every lower-case one. For a human-looking sort use key=str.lower — again with no brackets.
Your own key function
Any function taking one item and returning something comparable will do.
def surname(full_name): """The last word of a name - what a register sorts by.""" return full_name.split()[-1] pupils = ["Mia Tom", "Leo Reed", "Ana Rivera", "Hugo Kumar"] print(sorted(pupils)) print(sorted(pupils, key=surname))
Output
['Mia Tom', 'Hugo Kumar', 'Ana Rivera', 'Leo Reed'] ['Hugo Kumar', 'Ana Rivera', 'Mia Tom', 'Leo Reed']
That is Section 1 · Functions earning its place: a six-word function turns a first-name sort into a surname sort.
Why it matters. Leaderboards, price lists, registers, search results — all sorted, and almost never by the plain alphabet.
Worked Example · One Menu, Five Orders
12 minEach dish is a small list: name, price, minutes to cook. Sorting by any of them is one key function.
# menu_sort.py - the same menu, ordered five ways menu = [ ["pizza", 3.50, 5], ["pancake", 2.00, 3], ["noodles", 5.00, 8], ["ice cream", 4.90, 2], ["hot dog", 12.00, 15], ] def price_of(dish): return dish[1] def minutes_of(dish): return dish[2] def show(title, rows): print(title) for name, price, minutes in rows: print(f" {name:<14}{price:>7.2f}{minutes:>5} min") print() show("As typed", menu) show("By name", sorted(menu)) show("Cheapest first", sorted(menu, key=price_of)) show("Dearest first", sorted(menu, key=price_of, reverse=True)) show("Quickest first", sorted(menu, key=minutes_of)) # The original is still in its first order - sorted() never touched it print("First dish is still:", menu[0][0])
Output
As typed pizza 3.50 5 min pancake 2.00 3 min noodles 5.00 8 min ice cream 4.90 2 min hot dog 12.00 15 min By name ice cream 4.90 2 min noodles 5.00 8 min pizza 3.50 5 min pancake 2.00 3 min hot dog 12.00 15 min Cheapest first pancake 2.00 3 min pizza 3.50 5 min ice cream 4.90 2 min noodles 5.00 8 min hot dog 12.00 15 min Dearest first hot dog 12.00 15 min noodles 5.00 8 min ice cream 4.90 2 min pizza 3.50 5 min pancake 2.00 3 min Quickest first ice cream 4.90 2 min pizza 3.50 5 min pancake 2.00 3 min noodles 5.00 8 min hot dog 12.00 15 min First dish is still: pizza
Two things this shows:
- Sorting a list of lists compares the first item, then the second to break ties. That is why "By name" works with no key at all.
sorted()protects the original. Five different orders, andmenuis untouched at the end — which is why the last line still prints pizza.
Lesson 1 kept names and scores in two lists that had to stay lined up — sorting one would have broken the pairing. Keeping each record together as one small list makes sorting safe. Dictionaries do it better still.
Try It Yourself
13 minMake a list of eight classmates. Print them alphabetically, reverse-alphabetically, and shortest name first — without ever changing the original list. Print the original last to prove it.
Hint
names = ["Mia", "Leo", "Ana", "Hugo"] print(sorted(names)) print(sorted(names, reverse=True)) print(sorted(names, key=len)) print(names)
Make a list of six records, each [name, amount_saved, weeks].
- Print them sorted by amount, highest first.
- Print them sorted by amount saved per week — that needs a key function doing a division.
- Print the name of whoever saves fastest, taken from the sorted list.
- Sort the real list in place by name at the end, and print it to show the difference between
sortandsorted.
Mini-Challenge 🔥 · Debug: The Leaderboard That Lost Its Scores
8 minNia sorts a leaderboard. It prints None, then the names and scores no longer match. Find three mistakes.
# leaderboard.py - buggy
names = ["Leo", "Mia", "Ella", "Theo"]
scores = [88, 95, 72, 91]
top = scores.sort(reverse=True)
print("Ranking:", top)
for i in range(len(names)):
print(i + 1, names[i], scores[i])
print("Alphabetical:", names.sort(key=len()))Answer
scores.sort()returnsNone, sotopisNoneand the ranking line prints nothing useful. Either usesorted(scores, reverse=True), or sort in place and printscores.- Sorting one of two parallel lists breaks the pairing.
scoresis reordered whilenamesis not, so the loop prints Leo with Mia's score. The fix is to keep each pupil as one record —[name, score]— and sort those. key=len()calls the function instead of passing it, raisingTypeError: len() takes exactly one argument (0 given). It must bekey=len, with no brackets.
The working version, with records instead of parallel lists:
# leaderboard.py - fixed players = [ ["Leo", 88], ["Mia", 95], ["Ella", 72], ["Theo", 91], ] def score_of(player): return player[1] ranking = sorted(players, key=score_of, reverse=True) print("Ranking:") for i in range(len(ranking)): name, score = ranking[i] print(f" {i + 1}. {name:<10}{score:>4}") print() print("Alphabetical:", sorted(players, key=len))
Output
Ranking: 1. Mia 95 2. Theo 91 3. Leo 88 4. Ella 72 Alphabetical: [['Leo', 88], ['Mia', 95], ['Ella', 72], ['Theo', 91]]
The last line is a deliberate joke at len's expense — every record is 2 long, so nothing moves. Fixing it to sort by name is the exercise.
Recap
3 minitems.sort() reorders in place and returns None; sorted(items) returns a new list and leaves the original alone. reverse=True flips the order, and key= takes a function — passed without brackets — that says what to compare. Sorting parallel lists separately breaks them; keep each record together instead.
Vocabulary Card
- sort
- Method that reorders the list in place and returns
None. - sorted
- Built-in returning a new ordered list, original untouched.
- key function
- A function called on each item to produce the value being compared.
- stable sort
- Items that compare equal keep the order they were already in.
Homework
4 minWrite book_shelf.py. Store at least six books as records: [title, author, pages, price].
- Print the shelf as an aligned table in the order you typed it.
- Print it sorted by title, by page count, and by price with the dearest first — each with its own heading.
- Print it sorted by the author's surname, using a key function you write.
- Print the cheapest book's title on its own line, taken from a sorted list.
- At the very end, print the original list to show it never changed.
Sample · book_shelf.py
# book_shelf.py - one shelf, five orders, original preserved shelf = [ ["Village Boy", "Lat", 144, 24.00], ["Python for Kids", "Jason Briggs", 348, 79.90], ["The Hobbit", "J R R Tolkien", 310, 32.50], ["Bumi Manusia", "Pramoedya Ananta Toer", 535, 45.00], ["Ronggeng", "Ahmad Tohari", 200, 28.00], ["Tuesdays with Morrie", "Mitch Albom", 192, 39.90], ] def title_of(book): return book[0] def pages_of(book): return book[2] def price_of(book): return book[3] def surname_of(book): """Last word of the author name.""" return book[1].split()[-1] def show(heading, books): print(heading) print(f" {'Title':<22}{'Author':<24}{'Pages':>6}{'$':>8}") for title, author, pages, price in books: print(f" {title:<22}{author:<24}{pages:>6}{price:>8.2f}") print() show("As shelved", shelf) show("By title", sorted(shelf, key=title_of)) show("By pages", sorted(shelf, key=pages_of)) show("Dearest first", sorted(shelf, key=price_of, reverse=True)) show("By author surname", sorted(shelf, key=surname_of)) cheapest = sorted(shelf, key=price_of)[0] print("Cheapest book:", cheapest[0], f"at $ {cheapest[3]:.2f}") print() print("Original first book is still:", shelf[0][0])
Output
As shelved Title Author Pages $ Village Boy Lat 144 24.00 Python for Kids Jason Briggs 348 79.90 The Hobbit J R R Tolkien 310 32.50 Bumi Manusia Pramoedya Ananta Toer 535 45.00 Ronggeng Ahmad Tohari 200 28.00 Tuesdays with Morrie Mitch Albom 192 39.90 By author surname Title Author Pages $ Tuesdays with Morrie Mitch Albom 192 39.90 Python for Kids Jason Briggs 348 79.90 Village Boy Lat 144 24.00 The Hobbit J R R Tolkien 310 32.50 Ronggeng Ahmad Tohari 200 28.00 Bumi Manusia Pramoedya Ananta Toer 535 45.00 Cheapest book: Village Boy at $24.00 Original first book is still: Village Boy
Output trimmed to two tables. sorted(shelf, key=price_of)[0] is a neat idiom — sort, then take the first — but min(shelf, key=price_of) does the same job without sorting the whole list. Both are correct; the second is what you would write once you have met it.