Python
cmp_to_key
sorting
functools
key function

How does Python's cmp_to_key function work?

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

Python 3 sorting APIs are built around key functions, not comparison functions. If you still have older logic that compares two values directly, functools.cmp_to_key is the adapter that lets that code work with sorted and list.sort.

The utility is small, but the idea behind it is worth understanding. cmp_to_key does not magically make Python sort with your comparator directly; it wraps each item in an object that knows how to compare itself using your function.

Why Python Prefers Key Functions

A key function runs once per element and returns a value that Python can compare efficiently. For example, if you sort strings case-insensitively, the key can simply be str.lower.

python
names = ["Bob", "alice", "carol"]
print(sorted(names, key=str.lower))

This is usually faster and clearer than comparing pairs repeatedly. That is why Python 3 removed the old cmp argument from sorting APIs.

What cmp_to_key Actually Does

Your comparison function must accept two arguments and return:

  • a negative number if the first item should come before the second
  • zero if they are equal for sorting purposes
  • a positive number if the first item should come after the second

Then cmp_to_key converts that function into a key factory:

python
1from functools import cmp_to_key
2
3def compare_length_then_alpha(left, right):
4    if len(left) != len(right):
5        return len(left) - len(right)
6    if left < right:
7        return -1
8    if left > right:
9        return 1
10    return 0
11
12words = ["pear", "fig", "apple", "kiwi"]
13result = sorted(words, key=cmp_to_key(compare_length_then_alpha))
14print(result)

The important mental model is this:

  1. sorted calls cmp_to_key(compare_length_then_alpha).
  2. That returns a wrapper class.
  3. Each input value is wrapped in an instance of that class.
  4. When Python compares wrappers, the wrapper methods call your comparator.

So although the API now says key=..., the wrapped objects still use your comparison logic during ordering.

A Concrete Example

Suppose you need to sort version-like strings numerically by each dot-separated part. A plain key function is possible, but a comparator can be easier to read when rules are more involved.

python
1from functools import cmp_to_key
2
3def compare_versions(a, b):
4    left_parts = [int(part) for part in a.split(".")]
5    right_parts = [int(part) for part in b.split(".")]
6
7    max_len = max(len(left_parts), len(right_parts))
8    left_parts.extend([0] * (max_len - len(left_parts)))
9    right_parts.extend([0] * (max_len - len(right_parts)))
10
11    for left, right in zip(left_parts, right_parts):
12        if left != right:
13            return left - right
14    return 0
15
16versions = ["2.0", "1.9.9", "1.10", "1.2"]
17print(sorted(versions, key=cmp_to_key(compare_versions)))

This prints the versions in numeric order rather than string order.

What the Wrapper Looks Like Conceptually

You usually do not need the implementation details, but conceptually it looks like this:

python
1class K:
2    def __init__(self, obj, cmp):
3        self.obj = obj
4        self._cmp = cmp
5
6    def __lt__(self, other):
7        return self._cmp(self.obj, other.obj) < 0

The real implementation defines the rich comparison methods needed by the sort machinery. That is why your comparator must behave consistently. If it says a < b, b < c, and c < a, sorting becomes unstable or surprising.

When to Use It

Use cmp_to_key when:

  • you are porting Python 2 code
  • you already have a comparison function with non-trivial rules
  • converting the logic into a simple tuple key would make the code harder to read

If a normal key function is easy to write, prefer that. It is more idiomatic and often faster.

For example, instead of a comparator for sorting by length and then alphabetically, this key is better:

python
sorted(words, key=lambda item: (len(item), item))

That version is direct, deterministic, and does not require pairwise comparisons.

Common Pitfalls

  • Returning True or False instead of a negative, zero, or positive number. cmp_to_key expects numeric ordering semantics.
  • Writing a comparator that is inconsistent. Sorting assumes the comparison relation is stable.
  • Using cmp_to_key for cases where a tuple key would be simpler and faster.
  • Expecting cmp_to_key to call your comparator only once per item. Comparison-based sorting can call it many times.

Summary

  • 'cmp_to_key adapts an old-style two-argument comparator for Python 3 sorting APIs.'
  • It works by wrapping each item in an object whose comparison methods call your comparator.
  • A comparator must return negative, zero, or positive values, not booleans.
  • Prefer a normal key function when the ordering can be expressed clearly as a derived value.
  • Reach for cmp_to_key mainly for legacy code or genuinely complex comparison rules.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.