Сортировка по нескольким ключам в Python: короткий гайд
Как отсортировать список словарей сразу по нескольким полям — сначала по одному, потом по другому, да ещё в разных направлениях. Разбираем sorted, key, itemgetter и приём с reverse на конкретных данных.

Задача: есть список сотрудников, нужно отсортировать сначала по отделу (по алфавиту), а внутри отдела — по зарплате (от большей к меньшей). Один вызов sorted с правильным ключом решает это в одну строку. В конце вы будете уметь сортировать по любому числу полей и смешивать возрастание с убыванием. Займёт минут пятнадцать. Всё, что нужно, — Python 3.x (примеры проверены на 3.11 и 3.12) и интерпретатор под рукой.
Что нужно знать до начала
- Установленный Python 3 (
python3 --versionдолжна вернуть версию 3.8 и выше — приёмы работают на всей ветке 3.x). - Понимание, что
sorted()возвращает новый список, а метод списка.sort()меняет исходный на месте. - Данные, которые можно сравнивать: числа, строки, кортежи из них. Смешивать в одном ключе строку и число нельзя — получите
TypeError.
Как работает key
Аргумент key — это функция, которую Python вызывает для каждого элемента и по результату которой сравнивает элементы между собой. Если функция вернёт кортеж, Python сравнит кортежи поэлементно: сначала первые элементы, при равенстве — вторые, и так далее. Это и есть сортировка по нескольким ключам.
Шаг 1. Готовим данные
Возьмём список словарей — типичный результат выгрузки из базы или JSON.
employees = [
{"name": "Анна", "dept": "HR", "salary": 90000},
{"name": "Борис", "dept": "IT", "salary": 150000},
{"name": "Вера", "dept": "HR", "salary": 120000},
{"name": "Глеб", "dept": "IT", "salary": 150000},
{"name": "Дина", "dept": "IT", "salary": 130000},
]
Проверка: print(len(employees)) должен вывести 5.
Шаг 2. Сортировка по одному ключу
Сначала простой случай — по зарплате по возрастанию.
by_salary = sorted(employees, key=lambda e: e["salary"])
for e in by_salary:
print(e["name"], e["salary"])
Проверка: первой строкой выведется Анна 90000, последними — двое с зарплатой 150000.
Шаг 3. Сортировка по двум ключам сразу
Теперь главное: сначала по отделу (по алфавиту), внутри отдела — по зарплате (по возрастанию). Ключ возвращает кортеж из двух полей.
result = sorted(employees, key=lambda e: (e["dept"], e["salary"]))
for e in result:
print(e["dept"], e["name"], e["salary"])
Проверка: сначала пойдут все из HR (Анна 90000, затем Вера 120000), потом все из IT по возрастанию зарплаты. Порядок полей в кортеже определяет приоритет: первое поле — главное, второе разрешает ничьи.
Шаг 4. Разные направления для разных полей
Задача усложняется: отдел по алфавиту (по возрастанию), а зарплата внутри отдела — по убыванию. Общий reverse=True не подойдёт: он развернёт оба поля. Есть два рабочих приёма.
Приём А: минус перед числовым полем
Для чисел достаточно поставить минус — сортировка по возрастанию отрицательных значений даёт убывание исходных.
result = sorted(employees, key=lambda e: (e["dept"], -e["salary"]))
for e in result:
print(e["dept"], e["name"], e["salary"])
Проверка: внутри HR первой окажется Вера (120000), потом Анна (90000). Приём работает только с числами — со строкой минус не поставить.
Приём Б: двойная сортировка на стабильности
Сортировка в Python устойчива: элементы с равными ключами сохраняют исходный взаимный порядок. Это позволяет отсортировать поэтапно — сначала по менее приоритетному полю, потом по более приоритетному. Последняя сортировка задаёт главный порядок, а предыдущая сохраняется внутри групп.
# сначала по вторичному ключу — зарплата по убыванию
step1 = sorted(employees, key=lambda e: e["salary"], reverse=True)
# затем по главному — отдел по возрастанию
result = sorted(step1, key=lambda e: e["dept"])
for e in result:
print(e["dept"], e["name"], e["salary"])
Этот способ спасает, когда по убыванию надо сортировать строку. Минус — два прохода вместо одного, для больших списков это заметно.
Порядок ключей в кортеже — это порядок приоритетов. Первое поле решает, второе разрешает ничьи, третье разрешает ничьи между ничьими. Меняете порядок в кортеже — меняете смысл сортировки.
itemgetter вместо lambda
Когда сортируете по нескольким полям, operator.itemgetter читается чище и работает чуть быстрее lambda, потому что вызов уходит в C-код.
from operator import itemgetter
# эквивалент key=lambda e: (e["dept"], e["salary"])
result = sorted(employees, key=itemgetter("dept", "salary"))
У itemgetter есть ограничение: он не умеет ставить минус перед полем. Если нужны разные направления, комбинируйте itemgetter с приёмом двойной сортировки или возвращайтесь к lambda с минусом.
Сравнение приёмов
| Приём | Когда подходит | Ограничение |
|---|---|---|
| Кортеж в lambda | Все поля в одном направлении | reverse развернёт все поля сразу |
| Минус перед полем | Разные направления, поле числовое | Не работает со строками |
| Двойная сортировка | Разные направления, любые типы | Два прохода, медленнее на больших данных |
| itemgetter | Несколько полей, одно направление | Нет минуса и reverse по отдельному полю |
Если не получилось
TypeError: '<' not supported between instances of 'str' and 'int'— в одном поле лежат разные типы (где-то зарплата строка"150000"). Приведите к одному типу в ключе:key=lambda e: (e["dept"], int(e["salary"])).KeyError: 'salary'— не во всех словарях есть это поле. Дайте значение по умолчанию:key=lambda e: e.get("salary", 0).- Сортировка строк не по алфавиту — заглавные буквы идут раньше строчных из-за сравнения по кодам символов. Приводите к одному регистру:
key=lambda e: e["dept"].lower(). - Развернулись оба поля — вы поставили общий
reverse=Trueпри кортеже. Уберите его и используйте минус или двойную сортировку. - Изменился исходный список — вы вызвали
.sort()вместоsorted(). Метод сортирует на месте; если исходник нужен нетронутым, берите функциюsorted().
Что дальше
Если сортируете один и тот же список по разным наборам полей часто — вынесите ключи в именованные функции или заведите словарь {"by_dept": itemgetter("dept", "salary")}, чтобы не плодить lambda. Для сортировки объектов классов вместо словарей смотрите operator.attrgetter — он делает то же для атрибутов. А если данных десятки миллионов строк и сортировка стала узким местом — это уже отдельная тема про pandas и базы данных, здесь мы её не трогаем.
Логотип и фирменный стиль: Три концепции разного характера, палитра с обоснованием, проверка на читаемость в малом размере и в один цвет.
Спросить за 60 ₽Источники: Sorting Techniques — Python HOWTO (официальная документация), operator — Standard operators as functions (документация Python)
Частые вопросы
Чем sorted() отличается от метода .sort()?
А если у меня Python 3.6 или другая версия?
Как отсортировать по трём и более полям?
Почему минус нельзя поставить перед строковым полем?
Не сработала сортировка по дате в виде строки — почему?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.