Sorting with sorted() & key
Reviewed & published by Brayan K
Sorting in Python means ordering items with sorted() (which returns a new list) or list.sort() (which sorts in place), optionally guided by a key function that decides what to sort by.
Part of the free Python course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
Master the key argument and you can order anything — words by length, records by a field, or by several fields at once — using Python's fast, stable sort.
What You'll Learn in This Lesson
- sorted() (new list) vs list.sort() (in place)
- Reversing order with reverse=True
- Sorting by a computed key= function
- Lambdas, itemgetter, and attrgetter as keys
- Multi-key sorts with a tuple key
- Why Python's stable sort enables multi-pass sorting
1 sorted() vs list.sort() vs reverse=
sorted() hands back a new list and leaves the original alone; .sort() rearranges the list in place and returns None:
nums = [3, 1, 4, 1, 5, 9, 2]
# sorted() -> new list, original untouched
new_list = sorted(nums)
print(new_list) # [1, 1, 2, 3, 4, 5, 9]
print(nums) # [3, 1, 4, 1, 5, 9, 2] (unchanged)
# .sort() -> sorts in place, returns None
nums.sort()
print(nums) # [1, 1, 2, 3, 4, 5, 9]
# reverse=True for descending order
print(sorted([3, 1, 2], reverse=True)) # [3, 2, 1]
# sorted() works on ANY iterable (sort() is list-only)
print(sorted("dcba")) # ['a', 'b', 'c', 'd']# sorted() vs .sort() and reverse=
words = ["pear", "apple", "fig", "cherry"]
# sorted() returns a new list; original stays put
alpha = sorted(words)
print("Sorted:", alpha) # ['apple', 'cherry', 'fig', 'pear']
print("Original:", words) # ['pear', 'apple', 'fig', 'cherry']
# Descending with reverse=True
print(sorted([5, 2, 8, 1], reverse=True)) # [8, 5, 2, 1]
# .sort() mutates in place and returns None
nums = [4, 2, 6, 1]
result = nums.sort()
print("Returned:", result) # Returned: None
print("Now nums:", nums) # Now nums: [1, 2, 4, 6]
# ✅ Expected output:
# Sorted: ['apple', 'cherry', 'fig', 'pear']
# Original: ['pear', 'apple', 'fig', 'cherry']
# [8, 5, 2, 1]
# Returned: None
# Now nums: [1, 2, 4, 6]🔑 The key= Function
The key function tells Python what to sort by. It's called once per item, and the items are ordered by those computed values:
words = ["banana", "kiwi", "apple", "fig"]
# Sort by length, not alphabetically
print(sorted(words, key=len))
# ['fig', 'kiwi', 'apple', 'banana']
# Case-insensitive sort
names = ["bob", "Alice", "charlie", "Dave"]
print(sorted(names, key=str.lower))
# ['Alice', 'bob', 'charlie', 'Dave']
# A lambda for anything custom: sort by last character
print(sorted(words, key=lambda w: w[-1]))
# ['banana', 'apple', 'kiwi', 'fig']
# Sort dicts by a field
people = [{"name": "Ada", "age": 36}, {"name": "Ben", "age": 24}]
print(sorted(people, key=lambda p: p["age"]))
# [{'name': 'Ben', 'age': 24}, {'name': 'Ada', 'age': 36}]The elements themselves are never modified — key only computes the value to compare by. The returned list still holds your original items, just reordered.
# Custom ordering with key=
files = ["report.pdf", "a.txt", "summary.docx", "x.md"]
# Sort by filename length
print(sorted(files, key=len))
# ['x.md', 'a.txt', 'report.pdf', 'summary.docx']
# Sort numbers by distance from 10
nums = [3, 14, 8, 21, 9]
print(sorted(nums, key=lambda n: abs(n - 10)))
# [9, 8, 14, 3, 21]
# Sort records by a field, descending
scores = [("Ada", 88), ("Ben", 95), ("Cleo", 72)]
print(sorted(scores, key=lambda s: s[1], reverse=True))
# [('Ben', 95), ('Ada', 88), ('Cleo', 72)]
# ✅ Expected output:
# ['x.md', 'a.txt', 'report.pdf', 'summary.docx']
# [9, 8, 14, 3, 21]
# [('Ben', 95), ('Ada', 88), ('Cleo', 72)]2 itemgetter, attrgetter & Multi-Key Sorts
The operator module gives clean, fast key functions, and returning a tuple sorts by multiple fields at once:
from operator import itemgetter, attrgetter
people = [
{"last": "Smith", "first": "Ann", "age": 30},
{"last": "Smith", "first": "Bob", "age": 25},
{"last": "Adams", "first": "Zoe", "age": 40},
]
# itemgetter pulls dict keys (or list indices)
by_age = sorted(people, key=itemgetter("age"))
print([p["first"] for p in by_age]) # ['Bob', 'Ann', 'Zoe']
# Multi-key: sort by last name, then first name
by_name = sorted(people, key=itemgetter("last", "first"))
print([(p["last"], p["first"]) for p in by_name])
# [('Adams', 'Zoe'), ('Smith', 'Ann'), ('Smith', 'Bob')]from dataclasses import dataclass
from operator import attrgetter
@dataclass
class Player:
name: str
score: int
players = [Player("Ada", 90), Player("Ben", 90), Player("Cleo", 75)]
# attrgetter pulls object attributes
print([p.name for p in sorted(players, key=attrgetter("name"))])
# ['Ada', 'Ben', 'Cleo']
# Tuple key: highest score first, then name A-Z (negate score to flip)
ranked = sorted(players, key=lambda p: (-p.score, p.name))
print([(p.name, p.score) for p in ranked])
# [('Ada', 90), ('Ben', 90), ('Cleo', 75)]⚖️ Stable Sort & Multi-Pass Sorting
Python's sort is stable: items that compare equal keep their original order. That lets you sort in stages — minor key first, then major key:
data = [
("Ada", "B"),
("Ben", "A"),
("Cleo", "B"),
("Dan", "A"),
]
# Pass 1: sort by name (the minor key)
step1 = sorted(data, key=lambda x: x[0])
# Pass 2: sort by grade (the major key) - ties keep name order
step2 = sorted(step1, key=lambda x: x[1])
print(step2)
# [('Ben', 'A'), ('Dan', 'A'), ('Ada', 'B'), ('Cleo', 'B')]
# Within each grade, names stay alphabetical thanks to stabilityBecause equal elements never get shuffled, a second sort only reorders what the first one tied on. This staged approach is often clearer than one giant tuple key.
🎯 Your Turn: Fill in the Blanks
Replace each ___ to complete the sorts so the output matches.
# 🎯 YOUR TURN: replace each ___ to match the expected output
words = ["kiwi", "fig", "banana", "pear"]
# 1. Sort by length -> ['fig', 'kiwi', 'pear', 'banana']
print(sorted(words, ___))
# 2. Sort numbers descending -> [9, 5, 3, 1]
print(sorted([3, 9, 1, 5], ___))
# 3. Sort people by age, then name on ties
people = [("Ada", 30), ("Ben", 25), ("Cleo", 25)]
print(sorted(people, key=lambda p: (___)))
# -> [('Ben', 25), ('Cleo', 25), ('Ada', 30)]
# Expected output:
# ['fig', 'kiwi', 'pear', 'banana']
# [9, 5, 3, 1]
# [('Ben', 25), ('Cleo', 25), ('Ada', 30)]print(sorted(words, key=len))
print(sorted([3, 9, 1, 5], reverse=True))
print(sorted(people, key=lambda p: (p[1], p[0]))) # age, then nameCommon Errors & How to Fix Them
❌ Assigning the result of .sort()
nums = nums.sort() # nums is now None!✅ .sort() returns None. Call it on its own line, or use nums = sorted(nums).
❌ Calling the key function yourself
sorted(words, key=len(words)) # TypeError - pass the function, not a call✅ Pass the function object: key=len, not key=len(...). Python calls it for each item.
❌ Sorting mixed, uncomparable types
sorted([3, "a", 1]) # TypeError: '<' not supported between int and str✅ Give a key that maps everything to a comparable type, e.g. key=str, or clean the data first.
🎯 Mini Challenge: Leaderboard
Build a leaderboard: highest score first, ties broken alphabetically by name.
# Mini Challenge: rank players by score desc, then name asc
players = [
{"name": "Cleo", "score": 88},
{"name": "Ada", "score": 95},
{"name": "Ben", "score": 88},
{"name": "Dan", "score": 72},
]
# Tuple key: -score for descending, name for the tie-break
ranked = sorted(players, key=lambda p: (-p["score"], p["name"]))
for rank, p in enumerate(ranked, start=1):
print(f"{rank}. {p['name']} - {p['score']}")
# Expected output:
# 1. Ada - 95
# 2. Ben - 88
# 3. Cleo - 88
# 4. Dan - 72📚 Keep Learning
Go deeper with the official Python documentation:
- Sorting HOW TO — the official guide to key functions, stability, and the operator helpers.
- Built-in Functions — sorted() — the reference for the sorted() function and its arguments.
- operator — itemgetter & attrgetter — the fast, readable key-function builders.
Frequently Asked Questions
Lesson complete — you can order anything!
You know sorted() returns a new list while .sort() mutates in place, you can reverse, sort by a key (lambda, itemgetter, attrgetter), break ties with a tuple key, and lean on Python's stable sort for multi-pass ordering.
Practice quiz
What does sorted() return?
- None, after sorting in place
- The original list, now sorted
- A brand-new sorted list, leaving the original untouched
- A sorted tuple
Answer: A brand-new sorted list, leaving the original untouched. sorted() returns a new sorted list and does not modify the original iterable.
What does list.sort() return?
- None
- A new sorted list
- The sorted list
- The number of items sorted
Answer: None. .sort() sorts the list in place and returns None — never write x = x.sort().
What does sorted([3, 1, 2], reverse=True) produce?
- [1, 2, 3]
- [2, 1, 3]
- None
- [3, 2, 1]
Answer: [3, 2, 1]. reverse=True sorts in descending order, giving [3, 2, 1].
What is the role of the key argument?
- It filters out elements that don't match
- It is a function applied to each element to decide what to sort BY
- It sets the sort algorithm
- It reverses the order
Answer: It is a function applied to each element to decide what to sort BY. key=f computes f(item) for each element and sorts by those values; the elements themselves are unchanged.
What does sorted(['banana','kiwi','apple','fig'], key=len) return?
- ['fig','kiwi','apple','banana']
- ['apple','banana','fig','kiwi']
- ['banana','apple','kiwi','fig']
- ['kiwi','fig','apple','banana']
Answer: ['fig','kiwi','apple','banana']. Sorting by len orders by string length: fig(3), kiwi(4), apple(5), banana(6).
How do you sort by multiple fields at once?
- Call sorted() twice with the same key
- Pass reverse=True
- Return a tuple from the key function
- Use key=str only
Answer: Return a tuple from the key function. Returning a tuple like (p.last, p.first) makes Python compare element by element — primary then tie-breaker.
What does it mean that Python's sort is 'stable'?
- It never crashes
- Equal elements keep their original relative order
- It always uses the same amount of memory
- It sorts numbers before strings
Answer: Equal elements keep their original relative order. Stability means items that compare equal stay in their original order, which enables multi-pass sorting.
To rank by score descending then name ascending, which key works?
- key=lambda p: (p.score, p.name)
- key=lambda p: (p.name, p.score), reverse=True
- key=lambda p: p.score, p.name
- key=lambda p: (-p.score, p.name)
Answer: key=lambda p: (-p.score, p.name). Negating the numeric score flips just that field to descending while name stays ascending.
Why does sorted([3, 'a', 1]) raise a TypeError?
- The list is too short
- '<' is not supported between instances of int and str
- sorted() needs a key argument
- Lists cannot contain strings
Answer: '<' is not supported between instances of int and str. Python can't compare int and str directly. Provide a key that maps everything to a comparable type.
Which is true about sorted() vs .sort() regarding inputs?
- Both work on any iterable
- .sort() works on any iterable; sorted() is list-only
- sorted() works on any iterable; .sort() is a list-only method
- Neither works on strings
Answer: sorted() works on any iterable; .sort() is a list-only method. sorted() accepts any iterable (e.g. a string) and returns a list; .sort() exists only on lists.
Continue this course
- Previous: Unpacking & Star Expressions
- Next: Checkpoint: Core Syntax — Combine slicing, unpacking, sorting and match/case — then a quiz
- Quick reference: Python cheat sheet
Frequently asked questions
What's the difference between sorted() and list.sort()?
sorted() is a built-in function that takes any iterable and RETURNS a brand-new sorted list, leaving the original untouched. list.sort() is a method that sorts a list IN PLACE and returns None. Use sorted() when you want a new list or you're sorting something that isn't a list; use .sort() when you own the list and don't need the original order.
What does the key argument do?
key is a function applied to each element to decide what to sort BY. Python calls key(item) for every element and sorts by those computed values, not the elements themselves. So key=len sorts strings by length, key=str.lower sorts case-insensitively, and key=lambda p: p['age'] sorts dicts by their age field. The elements themselves are never changed.
How do I sort by more than one field?
Return a tuple from your key function. Python compares tuples element by element, so key=lambda p: (p.last, p.first) sorts by last name, then breaks ties by first name. To mix directions, sort by the secondary key first, or negate a numeric field like key=lambda p: (-p.score, p.name) for highest score first then name A-Z.
What does 'stable sort' mean and why does it matter?
A stable sort keeps equal elements in their original relative order. Python's sort is guaranteed stable, which lets you sort in stages: sort by a minor key first, then by a major key, and items that tie on the major key stay in the minor-key order. It's the reason multi-pass sorting works reliably.