Python • Algorithms and Complexity

sorted() with Key Functions — Use sorted() with key and reverse parameters

Master Python's sorted() function to arrange any data by custom rules using key functions and reverse ordering.

Code braces with terminal and markup symbols representing Python programming.
Inside this lesson 10 sections 5 questions 7 figures

SECTION 1

Historical Context & Motivation

Sorting is one of the oldest and most studied problems in computer science. From the earliest days of computing, programmers needed ways to arrange data in a meaningful order — alphabetizing names, ranking scores, or organizing files by date. Early sorting required writing dozens of lines of code, and customizing the sort order was even harder.

Python's sorted() function was designed to make sorting simple, readable, and powerful. It builds on decades of algorithm research — particularly the development of Timsort, a hybrid sorting algorithm created by Tim Peters specifically for Python. The key parameter was introduced to let programmers define custom sorting rules without rewriting the sort itself.

1945 • First Sorting Programs

John von Neumann writes one of the first recorded sorting programs for the EDVAC computer, establishing sorting as a fundamental computing task.

1991 • Python 1.0 Released

Guido van Rossum releases Python with built-in list sorting. Early versions use a comparison-based approach but lack the elegant key parameter.

2002 • Timsort Created

Tim Peters designs Timsort for Python 2.3 — a hybrid of merge sort and insertion sort that excels on real-world data. It becomes Python's default sorting algorithm.

2004 • sorted() with key Parameter

Python 2.4 introduces the built-in sorted() function with key and reverse parameters, replacing the older and less readable cmp approach.

2008 • Python 3 Standardizes Key Functions

Python 3 removes the cmp parameter entirely, making key the standard way to customize sorting — a cleaner, more Pythonic design.

The central question this lesson addresses is: how do you sort data by something other than the default order? What if you want to sort words by their length, students by their grade, or products by price? The key parameter gives you that power in a single, clean line of Python.

SECTION 2

Core Principles & Definitions

Before diving into examples, you need to understand a few foundational ideas about how sorted() works. Unlike the .sort() method which modifies a list in place, sorted() always returns a new list and leaves the original data unchanged. This makes it safe to use anywhere without worrying about side effects.

1

The sorted() Function

A built-in function that takes any iterable (list, tuple, string, etc.) and returns a new sorted list. Syntax: sorted(iterable, key=None, reverse=False)
2

The key Parameter

A function that is called on each element before comparison. It transforms elements for sorting purposes without changing the actual data in the output list.
3

The reverse Parameter

A boolean flag. When set to True, the sort order flips from ascending (smallest first) to descending (largest first).
4

Stability in Sorting

Python's sort is stable — when two elements have the same key value, they keep their original relative order. This matters when sorting by multiple criteria.
5

Lambda Functions

Small, anonymous functions written inline using lambda. They're commonly used as key functions because they're compact: lambda x: len(x)
✦

KEY TAKEAWAY

Think of the key function like a label maker. Imagine you have a pile of backpacks to arrange by weight. You don't rearrange the contents — you just stick a weight label on each one, sort by the labels, then line up the original backpacks in that order. The key function creates those temporary labels for sorting, but the items in your output list remain exactly as they were.

SECTION 3

Visual Explanation

The diagram below shows how sorted() with a key function works step by step. The original list of words is transformed by the key function (len), sorted by those transformed values, and then the original elements are returned in the new order.

How sorted(words, key=len) WorksSTEP 1: Original List"cat""elephant""go""bird"STEP 2: Apply key=len → Temporary Keys3len("cat")8len("elephant")2len("go")4len("bird")STEP 3: Sort by Key Values (2, 3, 4, 8)2348STEP 4: Return Original Elements in Sorted Order"go""cat""bird""elephant"Code:sorted(words,key=len)["go","cat","bird","elephant"]
The key function (len) is applied to each element to produce a temporary sorting key. Elements are sorted by these keys, then the original elements (not the keys) are returned.

Notice something important in the diagram: the key function is applied to every element exactly once. Python does not call the key function repeatedly during comparisons. Instead, it creates a decorated version of the list, sorts that, then strips the decorations away. This is both efficient and elegant.

SECTION 4

How It Works Under the Hood

Understanding the full signature of sorted() helps you use it with confidence. The function signature is straightforward, and each parameter has a clear role.

FUNCTION SIGNATURE

sorted(iterable,key=None,reverse=False)sorted(iterable, key=None, reverse=False)

iterable — any sequence (list, tuple, string, dict, set, generator). key — a function that takes one element and returns a comparison value. reverse — if True, the sort is in descending order.

The Decorate-Sort-Undecorate Pattern

Internally, Python uses a pattern called decorate-sort-undecorate (also known as the Schwartzian transform in other languages). Here is how it works in three phases:

  1. Decorate: For each element x in the iterable, compute key(x) and pair it with the original element to form (key(x), x).
  2. Sort: Sort the pairs by the first value (the key) using Timsort.
  3. Undecorate: Extract the original elements from the sorted pairs and return them as the final list.

TIME COMPLEXITY

O(nlogn)comparisons+O(n)keyevaluationsO(n log n) comparisons + O(n) key evaluations

The key function is called exactly n times (once per element), not during every comparison. This is why key functions are efficient — even if sorting makes O(n log n) comparisons, the key is only computed O(n) times.

Common Key Functions

Key FunctionWhat It DoesExample
key=lenSorts by the length of each elementsorted(["hi", "hello", "hey"], key=len)
key=str.lowerCase-insensitive alphabetical sortsorted(["Banana", "apple"], key=str.lower)
key=absSorts by absolute value (distance from zero)sorted([-5, 3, -1, 4], key=abs)
key=lambda x: x[1]Sorts tuples/lists by their second elementsorted([("A", 3), ("B", 1)], key=lambda x: x[1])
key=lambda s: s.split()[-1]Sorts strings by their last wordsorted(["John Smith", "Ada Lovelace"], key=lambda s: s.split()[-1])

Commonly used key functions with sorted()

SECTION 5

Sorting Complex Data Structures

The real power of key functions emerges when you work with complex data structures — lists of dictionaries, lists of tuples, or lists of custom objects. In real-world programs, you rarely sort simple lists of numbers. Instead, you sort records with multiple fields, and you need to choose which field to sort by.

Sorting a List of Dictionaries by Different KeysOriginal Data: students[{"name":"Zara", "grade":92}, {"name":"Alex", "grade":88}, {"name":"Maya", "grade":95}]Sort by Name (Alphabetical)sorted(students, key=lambda s: s["name"])Result:Alex→Maya→ZaraA → M → Z (ascending)Sort by Grade (Descending)sorted(students, key=lambda s: s["grade"], reverse=True)Result:Maya:95→Zara:92→Alex:8895 → 92 → 88 (descending)Multi-Level Sort: By Grade (desc), Then by Name (asc) — Using Two Passesstudents = [{"name":"Zara","grade":92}, {"name":"Alex","grade":92}, {"name":"Maya","grade":95}]step1 = sorted(students, key=lambda s: s["name"])↑ Sort by secondary key first (name ascending)step2 = sorted(step1, key=lambda s: s["grade"], reverse=True)↑ Then sort by primary key (grade descending). Stability preserves name order within ties!Result: [{"name":"Maya","grade":95}, {"name":"Alex","grade":92}, {"name":"Zara","grade":92}]
Two ways to sort dictionaries: by a single key (name or grade), or by multiple keys using two stable sort passes. Python's stable sort guarantees that ties in the primary sort preserve the order from the secondary sort.
💡

Pro Tip: Tuple Keys for Multi-Level Sorting

Instead of using two sort passes, you can return a tuple from your key function. Python compares tuples element by element, so key=lambda s: (-s["grade"], s["name"]) sorts by grade descending (using negation) and then by name ascending, all in one call. The negative sign trick only works with numbers, though.

SECTION 6

Worked Example

Let's walk through a complete, realistic example. Suppose you have a list of video games with their titles and ratings, and you want to display them sorted from highest to lowest rating.

Sorting Video Games by Rating (Descending)

  1. 1

    Step 1 — Define the Data

    We start with a list of tuples, where each tuple holds a game title and its rating out of 10: games = [("Zelda", 9.7), ("Minecraft", 9.2), ("Fortnite", 8.5), ("Elden Ring", 9.5), ("Among Us", 8.0)]
  2. 2

    Step 2 — Identify the Key

    Each element is a tuple like ("Zelda", 9.7). We want to sort by the rating, which is at index 1. Our key function will be lambda game: game[1]. This extracts the second element of each tuple.
  3. 3

    Step 3 — Choose the Sort Order

    We want the highest rating first, so we set reverse=True. Without this, the default is ascending — lowest first.
  4. 4

    Step 4 — Write the Code

    ranked = sorted(games, key=lambda game: game[1], reverse=True)
  5. 5

    Step 5 — Trace the Execution

    Python calls the lambda on each tuple: 9.7, 9.2, 8.5, 9.5, 8.0. It sorts these values in descending order: 9.7, 9.5, 9.2, 8.5, 8.0. Then it returns the original tuples in this new order.
    [("Zelda", 9.7), ("Elden Ring", 9.5), ("Minecraft", 9.2), ("Fortnite", 8.5), ("Among Us", 8.0)]
  6. 6

    Step 6 — Verify the Original is Unchanged

    The original games list is still in its original order. sorted() created a new list called ranked without modifying the input.
⚠

Common Mistake

Don't write key=lambda game: game[1]() with parentheses after game[1]. The value 9.7 is a float, not a function — you can't call it. Also, don't write key=len() — pass the function itself (key=len), not the result of calling it.

SECTION 7

sorted() vs .sort() and Other Approaches

Python offers several ways to sort data. Understanding when to use each approach is part of becoming a skilled programmer. The two main options are the built-in sorted() function and the list method .sort(). They share the same key and reverse parameters but behave differently in important ways.

Featuresorted(iterable)list.sort()
ReturnsA new sorted listNone (modifies list in place)
Input typesAny iterable (list, tuple, string, dict, set, generator)Lists only
Original dataUnchanged (safe)Overwritten (destructive)
MemoryUses extra memory for the new listSorts in place — more memory efficient
ChainingCan be chained: sorted(data, key=len)[0]Cannot chain (returns None)
Best forWhen you need the original + sorted version, or sorting non-list iterablesWhen you only need the sorted version and want to save memory

Key differences between sorted() and .sort()

✦

WHEN TO USE WHICH

Think of sorted() like making a photocopy of a document and then rearranging the copy — your original stays safe in the drawer. Think of .sort() like rearranging the pages of the original document itself. Use sorted() when in doubt — it's safer and more flexible.

SECTION 8

Connection to Advanced Concepts

The key parameter in sorted() is your first encounter with a powerful programming concept: higher-order functions — functions that accept other functions as arguments. This idea appears throughout Python and computer science more broadly.

This LessonAdvanced ConceptConnection
key=lambda x: x[1]Higher-order functionsPassing functions as arguments is foundational in functional programming
key=lenmap(), filter()These built-in functions also accept a function as their first argument
Sort stabilityMerge sort, Timsort internalsUnderstanding why stability matters leads to studying sorting algorithms
O(n log n) complexityBig-O analysisKnowing that sorting is O(n log n) is foundational to algorithm analysis
Lambda expressionsClosures, decoratorsLambdas are a stepping stone to more advanced function techniques in Python

How sorted() concepts connect to advanced topics

As you advance in computer science, you'll encounter the operator module, which provides functions like operator.itemgetter() and operator.attrgetter() as faster, more readable alternatives to lambda functions for common key patterns. You'll also learn about custom comparison methods like __lt__ that let your own classes define how their instances should be sorted.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

What is the difference between sorted(words) and sorted(words, key=len)? Explain what each call sorts by and why the results would differ for the list ["banana", "fig", "cherry", "apple"].
Show answer
sorted(words) sorts alphabetically (the default for strings), producing ["apple", "banana", "cherry", "fig"]. In contrast, sorted(words, key=len) sorts by the length of each string, producing ["fig", "apple", "banana", "cherry"] (lengths: 3, 5, 6, 6). Without a key function, Python compares elements directly using their natural ordering. With key=len, Python compares the return values of len() instead. Note that "banana" and "cherry" both have length 6, and since the sort is stable, they keep their original relative order.

Question 2: BASIC CALCULATION

What does the following code output? Trace through it step by step. nums = [15, -3, 8, -12, 4] result = sorted(nums, key=abs) print(result)
Show answer
First, the key function abs is applied to each element: abs(15)=15, abs(-3)=3, abs(8)=8, abs(-12)=12, abs(4)=4. Sorting these key values in ascending order gives: 3, 4, 8, 12, 15. The corresponding original elements in this order are: [-3, 4, 8, -12, 15]. The output is [-3, 4, 8, -12, 15]. Notice that the original negative signs are preserved — the key function only affects sorting order, not the values themselves.

Question 3: INTERMEDIATE

You have a list of tuples representing students and their test scores: students = [("Emma", 91), ("Liam", 85), ("Sophia", 91), ("Noah", 78)] Write a single sorted() call that sorts students by score from highest to lowest. For students with the same score, sort alphabetically by name (ascending).
Show answer
The solution uses a tuple key with negation: sorted(students, key=lambda s: (-s[1], s[0])). The negative sign on s[1] reverses the score order (highest first), while s[0] sorts names alphabetically for tied scores. The result is [("Emma", 91), ("Sophia", 91), ("Liam", 85), ("Noah", 78)]. Emma comes before Sophia because 'E' < 'S' alphabetically, and both share the score 91.

Question 4: APPLIED

You're building a playlist app. Songs are stored as dictionaries: songs = [ {"title": "Blinding Lights", "artist": "The Weeknd", "plays": 3200}, {"title": "Levitating", "artist": "Dua Lipa", "plays": 2800}, {"title": "Stay", "artist": "The Kid LAROI", "plays": 3200}, {"title": "Peaches", "artist": "Justin Bieber", "plays": 1500} ] Write code to display the top songs sorted by play count (most played first). For songs with equal plays, sort alphabetically by title.
Show answer
The solution is: top_songs = sorted(songs, key=lambda s: (-s["plays"], s["title"])). The negated play count puts the most-played songs first, and the title string handles alphabetical tie-breaking. The result order is: "Blinding Lights" (3200), "Stay" (3200), "Levitating" (2800), "Peaches" (1500). Notice "Blinding Lights" comes before "Stay" because 'B' < 'S'. You could also solve this with two passes using sort stability, but the tuple key approach is more concise and runs in a single pass.

Question 5: CRITICAL THINKING

A classmate writes this code to sort a list of strings case-insensitively in reverse order: words = ["Banana", "apple", "Cherry"] result = sorted(words, key=str.upper, reverse=True) Another classmate argues that key=str.lower should be used instead. Does it matter? Would the output differ? Also, explain why the original strings in the result are mixed-case even though the key converts them. What would happen if someone accidentally wrote key=str.upper() instead?
Show answer
Both str.upper and str.lower produce the same sorted order here because both normalize case before comparison — "APPLE" vs "BANANA" vs "CHERRY" or "apple" vs "banana" vs "cherry" rank the same way. With reverse=True, the result is ["Cherry", "Banana", "apple"] in both cases. The original strings keep their mixed casing because the key function is only used for comparisons — it doesn't transform the actual output. Writing key=str.upper() would cause a TypeError because str.upper() without an argument tries to call the method on the str class itself, not on each individual string. The key parameter expects a function reference, not the result of calling a function.

SUMMARY

Summary

Summary

Python's sorted() function returns a new sorted list from any iterable without modifying the original data. The key parameter accepts a function that transforms each element into a comparison value — like key=len for sorting by length or key=lambda x: x[1] for sorting by the second element of a tuple. The reverse=True parameter flips the order from ascending to descending.

Key functions are called exactly once per element (not during every comparison), making them efficient at O(n) key evaluations plus O(n log n) comparisons. Python's stable sort guarantees that equal elements keep their original order, enabling multi-level sorting through either tuple keys or successive sort passes. Mastering sorted() with key functions is your gateway to higher-order functions and functional programming patterns throughout Python.

Varsity Tutors • Python • sorted() with Key Functions — Use sorted() with key and reverse parameters