Python: уроки, справочник и задачи

Как отсортировать словарь в Python

Раздел: Словари в Python · Обновлено 25.10.2026
Код в редакторе на темном экране
Фото: Ilya Pavlov, CC0

Начиная с Python 3.7 словарь гарантированно сохраняет порядок вставки ключей, поэтому "отсортированный словарь" стал вполне реальной вещью: достаточно собрать новый словарь из пар в нужном порядке. При этом у самого dict нет метода sort(), и вся работа идет через встроенную функцию sorted. Ниже разобраны сортировка по ключу и по значению, по убыванию, по нескольким критериям, сортировка вложенных словарей и списков словарей, а также быстрый способ получить топ значений через Counter.

Как устроена сортировка словаря

Функция sorted(iterable, key=None, reverse=False) принимает любой итерируемый объект и возвращает новый список. Словарь при итерации отдает ключи, поэтому sorted(d) вернет отсортированный список ключей, а не словарь. Чтобы получить именно словарь, сортируют пары d.items() и передают результат в конструктор dict.

prices = {"banana": 89, "apple": 120, "cherry": 450, "kiwi": 35}

print(sorted(prices))            # -> ['apple', 'banana', 'cherry', 'kiwi']
print(sorted(prices.items()))
# -> [('apple', 120), ('banana', 89), ('cherry', 450), ('kiwi', 35)]

by_key = dict(sorted(prices.items()))
print(by_key)
# -> {'apple': 120, 'banana': 89, 'cherry': 450, 'kiwi': 35}

Исходный словарь при этом не меняется. Если нужно "отсортировать на месте", просто переприсвойте переменную или обновите содержимое: prices = dict(sorted(prices.items())). Про устройство словарей и их методы подробно рассказано в статье Словари в Python.

Сортировка словаря по ключу

Кортежи сравниваются поэлементно: сначала первые элементы, при равенстве вторые. Ключи словаря уникальны, поэтому sorted(d.items()) сортирует пары ровно по ключу, до значений дело не доходит. Это самый короткий способ отсортировать словарь по ключам.

grades = {"Петров": 4, "Алексеева": 5, "Смирнов": 3, "Борисов": 5}

print(dict(sorted(grades.items())))
# -> {'Алексеева': 5, 'Борисов': 5, 'Петров': 4, 'Смирнов': 3}

# по убыванию ключей
print(dict(sorted(grades.items(), reverse=True)))
# -> {'Смирнов': 3, 'Петров': 4, 'Борисов': 5, 'Алексеева': 5}

Ключи в разном регистре и числа-строки

Строки сравниваются по кодам символов, поэтому заглавные буквы идут раньше строчных, а строка "10" оказывается раньше "9". Для таких случаев нужен параметр key, который преобразует элемент перед сравнением.

d = {"beta": 2, "Alpha": 1, "gamma": 3, "Delta": 4}
print(dict(sorted(d.items())))
# -> {'Alpha': 1, 'Delta': 4, 'beta': 2, 'gamma': 3}
print(dict(sorted(d.items(), key=lambda kv: kv[0].lower())))
# -> {'Alpha': 1, 'beta': 2, 'Delta': 4, 'gamma': 3}

pages = {"10": "Итоги", "2": "Введение", "1": "Обложка"}
print(dict(sorted(pages.items(), key=lambda kv: int(kv[0]))))
# -> {'1': 'Обложка', '2': 'Введение', '10': 'Итоги'}

Буква "ё" в кодировке Unicode стоит после "я", поэтому простая сортировка русских слов с "ё" дает неожиданный порядок. Если это важно, подмените ее в ключе: key=lambda kv: kv[0].lower().replace("ё", "е").

Сортировка словаря по значению

Самый частый запрос. Параметр key получает каждую пару (ключ, значение) и должен вернуть то, по чему сортировать. Для значения это второй элемент кортежа, kv[1].

prices = {"banana": 89, "apple": 120, "cherry": 450, "kiwi": 35}

cheap_first = dict(sorted(prices.items(), key=lambda kv: kv[1]))
print(cheap_first)
# -> {'kiwi': 35, 'banana': 89, 'apple': 120, 'cherry': 450}

expensive_first = dict(sorted(prices.items(), key=lambda kv: kv[1], reverse=True))
print(expensive_first)
# -> {'cherry': 450, 'apple': 120, 'banana': 89, 'kiwi': 35}

Если нужны только ключи в порядке значений, можно сортировать сам словарь, передав в качестве key метод get: sorted(prices, key=prices.get) вернет ['kiwi', 'banana', 'apple', 'cherry']. Запись короткая и не создает промежуточных кортежей. Подробнее о синтаксисе lambda в статье Лямбда функция в Python.

operator.itemgetter вместо lambda

Модуль operator предлагает готовую функцию itemgetter(i), которая делает то же, что lambda x: x[i]. Она реализована на C, работает немного быстрее и, по мнению многих, читается чище. С несколькими аргументами она возвращает кортеж, что удобно для сортировки по нескольким полям.

from operator import itemgetter

prices = {"banana": 89, "apple": 120, "cherry": 450, "kiwi": 35}
print(dict(sorted(prices.items(), key=itemgetter(1))))
# -> {'kiwi': 35, 'banana': 89, 'apple': 120, 'cherry': 450}

get_name_age = itemgetter("name", "age")
print(get_name_age({"name": "Анна", "age": 31, "city": "Казань"}))
# -> ('Анна', 31)

Сортировка по нескольким критериям

Чтобы сортировать по значению, а при равных значениях по ключу, верните из key кортеж. Python сравнит кортежи поэлементно.

scores = {"Олег": 87, "Анна": 95, "Ирина": 87, "Борис": 95, "Вера": 70}

ranked = sorted(scores.items(), key=lambda kv: (-kv[1], kv[0]))
for name, score in ranked:
    print(f"{score:>3} {name}")
# ->  95 Анна
# ->  95 Борис
# ->  87 Ирина
# ->  87 Олег
# ->  70 Вера

Минус перед числом дает убывание по баллам, а имена при этом идут по возрастанию. Трюк работает только для чисел. Если нужно сортировать по убыванию строк и по возрастанию чисел, используйте устойчивость сортировки: Python гарантирует, что элементы с равным ключом сохраняют взаимный порядок. Значит, можно отсортировать дважды, начиная со второстепенного критерия.

items = list(scores.items())
items.sort(key=itemgetter(0), reverse=True)   # второстепенный: имя по убыванию
items.sort(key=itemgetter(1))                 # главный: балл по возрастанию
print(items)
# -> [('Вера', 70), ('Олег', 87), ('Ирина', 87), ('Борис', 95), ('Анна', 95)]

Разница между list.sort() и sorted(), а также подробности про устойчивость сортировки описаны в статье Python List sort.

Сортировка значений внутри словаря

Иногда нужно упорядочить не пары словаря, а сами значения, когда это списки. Например, в словаре хранятся оценки каждого студента, и для отчета их нужно вывести по возрастанию. Здесь порядок ключей не меняется, а каждый список сортируется отдельно. Если исходные данные нельзя трогать, создайте новый словарь через генератор словаря, если можно, отсортируйте списки на месте методом sort.

marks = {"Анна": [5, 3, 4], "Борис": [4, 4, 2], "Вера": [3, 5, 5]}

sorted_marks = {name: sorted(values) for name, values in marks.items()}
print(sorted_marks)
# -> {'Анна': [3, 4, 5], 'Борис': [2, 4, 4], 'Вера': [3, 5, 5]}

for values in marks.values():
    values.sort(reverse=True)
print(marks)
# -> {'Анна': [5, 4, 3], 'Борис': [4, 4, 2], 'Вера': [5, 5, 3]}

Обе операции можно совместить: отсортировать студентов по среднему баллу и вывести каждому отсортированный список. В качестве ключа сортировки тогда выступает выражение, вычисленное по значению, например sum(v) / len(v). Ключевая функция вызывается ровно один раз для каждого элемента, поэтому даже дорогие вычисления в ней не умножаются на количество сравнений.

from statistics import mean

marks = {"Анна": [5, 3, 4], "Борис": [4, 4, 2], "Вера": [3, 5, 5]}
for name, values in sorted(marks.items(), key=lambda kv: mean(kv[1]), reverse=True):
    print(f"{name}: {mean(values):.2f}")
# -> Вера: 4.33
# -> Анна: 4.00
# -> Борис: 3.33

Сортировка по датам и по длине

Даты в строковом виде сортируются корректно только в формате ISO ГГГГ-ММ-ДД, где старшие разряды стоят слева. Формат ДД.ММ.ГГГГ при обычной строковой сортировке перемешает годы, поэтому строку нужно разобрать в объект date прямо в ключе.

from datetime import datetime

deadlines = {"отчет": "15.11.2026", "релиз": "03.12.2025", "аудит": "01.02.2026"}

by_date = dict(sorted(
    deadlines.items(),
    key=lambda kv: datetime.strptime(kv[1], "%d.%m.%Y"),
))
print(by_date)
# -> {'релиз': '03.12.2025', 'аудит': '01.02.2026', 'отчет': '15.11.2026'}

words = {"кот": 3, "гиппопотам": 1, "слон": 7}
print(sorted(words, key=len))   # -> ['кот', 'слон', 'гиппопотам']

В последней строке ключом служит встроенная функция len: в key можно передать любую функцию одного аргумента, не только lambda. Так же работают str.lower, abs или собственная функция с понятным именем, которая к тому же легко тестируется отдельно.

Шпаргалка по способам

ЗадачаКод
Словарь по ключамdict(sorted(d.items()))
Словарь по ключам, убываниеdict(sorted(d.items(), reverse=True))
Словарь по значениямdict(sorted(d.items(), key=itemgetter(1)))
Словарь по значениям, убываниеdict(sorted(d.items(), key=itemgetter(1), reverse=True))
Список ключей по значениямsorted(d, key=d.get)
Топ-3 по значениюheapq.nlargest(3, d.items(), key=itemgetter(1))
Ключ с максимальным значениемmax(d, key=d.get)
Частоты, самые частые первымиCounter(data).most_common()

Вложенные словари и списки словарей

В реальных данных значения редко бывают простыми числами. Чаще это вложенный словарь с полями, например результат разбора JSON. Принцип тот же: в key вы достаете нужное поле.

users = {
    "u1": {"name": "Анна", "age": 31, "orders": 5},
    "u2": {"name": "Борис", "age": 25, "orders": 12},
    "u3": {"name": "Вера", "age": 42, "orders": 5},
}

by_orders = dict(sorted(users.items(), key=lambda kv: kv[1]["orders"], reverse=True))
print(list(by_orders))     # -> ['u2', 'u1', 'u3']

by_orders_then_age = sorted(
    users.items(),
    key=lambda kv: (-kv[1]["orders"], kv[1]["age"]),
)
print([uid for uid, _ in by_orders_then_age])   # -> ['u2', 'u1', 'u3']

Сортировка списка словарей

Список словарей сортируется проще, потому что каждый элемент уже является словарем, и itemgetter принимает имя поля напрямую.

from operator import itemgetter

rows = [
    {"city": "Казань", "temp": 12},
    {"city": "Сочи", "temp": 19},
    {"city": "Мурманск", "temp": 2},
]
rows.sort(key=itemgetter("temp"), reverse=True)
print([r["city"] for r in rows])   # -> ['Сочи', 'Казань', 'Мурманск']

Если поле может отсутствовать или быть None

Когда у части записей нет поля или оно равно None, сравнение упадет с TypeError: '<' not supported between instances of 'NoneType' and 'int'. Решение: в ключе вернуть кортеж, где первый элемент отправляет пустые значения в конец.

rows = [{"n": "a", "v": 3}, {"n": "b"}, {"n": "c", "v": None}, {"n": "d", "v": 1}]

def by_v(row):
    v = row.get("v")
    return (v is None, v if v is not None else 0)

print([r["n"] for r in sorted(rows, key=by_v)])   # -> ['d', 'a', 'b', 'c']

False меньше True, поэтому записи с заполненным полем идут первыми, а пустые в конце в исходном порядке.

Counter.most_common: топ значений одной строкой

Частая задача: посчитать, сколько раз встречается каждое слово или каждый статус, и вывести самые частые. Для нее есть collections.Counter. Это подкласс словаря, а его метод most_common(n) возвращает список пар, отсортированный по убыванию частоты.

from collections import Counter

statuses = ["200", "404", "200", "500", "200", "404", "301"]
c = Counter(statuses)
print(c)                    # -> Counter({'200': 3, '404': 2, '500': 1, '301': 1})
print(c.most_common(2))     # -> [('200', 3), ('404', 2)]
print(c.most_common()[-1])  # -> ('301', 1) (самый редкий)

text = "мама мыла раму мама мыла пол"
print(Counter(text.split()).most_common(1))   # -> [('мама', 2)]

При равной частоте элементы идут в порядке первого появления. Если у вас уже есть обычный словарь с количествами, оберните его: Counter(d).most_common(5). Внутри для небольшого n используется heapq.nlargest, что быстрее полной сортировки на больших данных. Остальные структуры модуля разобраны в статье модуль collections в Python.

Практический пример: отчет по продажам

Соберем приемы в одну небольшую задачу. Есть список продаж за день, каждая продажа - это пара "товар, сумма". Нужно посчитать выручку по каждому товару, вывести товары от самого прибыльного к наименее прибыльному, а при равной выручке упорядочить их по названию. Отдельно нужно показать три самых популярных товара по количеству продаж.

from collections import Counter, defaultdict

sales = [
    ("чай", 250), ("кофе", 320), ("чай", 250), ("сок", 180),
    ("кофе", 320), ("вода", 90), ("сок", 320), ("вода", 90),
]

revenue = defaultdict(int)
for product, amount in sales:
    revenue[product] += amount

report = sorted(revenue.items(), key=lambda kv: (-kv[1], kv[0]))
for product, total in report:
    print(f"{product:<6}{total:>6}")
# -> кофе     640
# -> сок      500
# -> чай      500
# -> вода     180

popular = Counter(product for product, _ in sales).most_common(3)
print(popular)   # -> [('чай', 2), ('кофе', 2), ('сок', 2)]

Обратите внимание на несколько деталей. Для накопления сумм использован defaultdict(int), который избавляет от проверки наличия ключа. Сортировка выполняется один раз в конце, а не после каждого добавления, потому что промежуточный порядок никому не нужен. Сок и чай набрали одинаковую выручку, и вторичный критерий по названию сделал порядок предсказуемым: без него результат зависел бы от порядка, в котором товары впервые встретились в данных. В выдаче most_common при равной частоте сохраняется порядок первого появления, поэтому "вода" с теми же двумя продажами не попала в тройку. Если такое поведение не устраивает, сортируйте пары из Counter сами с нужным вторичным ключом.

Производительность и когда сортировать не нужно

Сортировка занимает O(n log n). Для словаря на сотни тысяч записей это доли секунды, но в цикле может стать узким местом. Несколько практических правил:

import heapq
from operator import itemgetter

sales = {f"item{i}": (i * 7919) % 1000 for i in range(100_000)}
top3 = heapq.nlargest(3, sales.items(), key=itemgetter(1))
print(top3)

Частые ошибки

Частые вопросы

Сохранит ли словарь порядок после сортировки?

Да. Словарь, созданный через dict(sorted(...)), хранит ключи в том порядке, в котором их вставили. Но если вы потом добавите новый ключ, он окажется в конце, и порядок придется восстанавливать повторной сортировкой.

Как отсортировать словарь по значению по убыванию?

dict(sorted(d.items(), key=lambda kv: kv[1], reverse=True)). Для чисел можно также писать key=lambda kv: -kv[1], результат будет тем же, но вариант с reverse понятнее и работает для любых типов.

Что лучше: lambda или itemgetter?

По результату они одинаковы. itemgetter немного быстрее и удобен для выборки нескольких полей, lambda гибче, когда нужно преобразование: регистр, приведение к числу, обработка None. Выбирайте то, что читается лучше в конкретном месте.

Как получить первые N элементов отсортированного словаря?

Срежьте список пар перед созданием словаря: dict(sorted(d.items(), key=itemgetter(1), reverse=True)[:5]). Для больших словарей быстрее dict(heapq.nlargest(5, d.items(), key=itemgetter(1))).

Как отсортировать ключи при сохранении в JSON?

Сортировать словарь заранее не нужно: передайте sort_keys=True в json.dumps или json.dump, и ключи будут упорядочены на всех уровнях вложенности.

Как отсортировать словарь, если ключи разных типов?

Прямое сравнение числа и строки в Python 3 запрещено, поэтому sorted(d.items()) упадет с TypeError. Задайте ключ, который сначала группирует по типу, а потом сравнивает значения внутри группы: key=lambda kv: (isinstance(kv[0], str), str(kv[0])). Числовые ключи окажутся первыми, строковые после них. Если ключи на самом деле должны быть одного типа, лучше исправить данные при загрузке, чем маскировать проблему в сортировке.