Списки словарей в Python: короткое руководство
Данные из API и БД почти всегда приходят как список словарей. Разбираем группировку, сортировку, фильтрацию и поиск по такой структуре в Python 3.11+.

Что вы получите в конце
Список словарей — то, во что превращается любой ответ API после response.json() и любая выборка из базы через cursor.fetchall() с DictCursor. Структура вида [{"name": "Анна", "dept": "sales"}, ...]. К концу руководства вы будете уверенно сортировать такой список по любому полю, группировать по ключу, фильтровать и превращать в удобный индекс по id. Времени на прочтение и повтор примеров — около 15 минут.
Всё ниже проверено на Python 3.11. Функция itertools.batched из последнего раздела появилась только в 3.12 — про это сказано отдельно. Ничего, кроме стандартной библиотеки, ставить не нужно: operator и itertools входят в поставку.
Для примеров возьмём один набор данных:
employees = [
{"id": 3, "name": "Анна", "dept": "sales", "salary": 90000},
{"id": 1, "name": "Борис", "dept": "it", "salary": 120000},
{"id": 2, "name": "Вера", "dept": "sales", "salary": 95000},
{"id": 4, "name": "Глеб", "dept": "it", "salary": 110000},
]
Достать значения одного поля
Самая частая операция — вытащить все значения по одному ключу. List comprehension читается лучше, чем map, и работает не медленнее:
names = [emp["name"] for emp in employees]
# ['Анна', 'Борис', 'Вера', 'Глеб']Проблема начинается, когда ключа может не быть в части словарей. Обращение emp["salary"] бросит KeyError на первой же записи без этого ключа. Используйте .get() со значением по умолчанию:
# Если ключа нет — вернётся 0, а не исключение
salaries = [emp.get("salary", 0) for emp in employees]
Сортировка по полю
Сортировать удобнее всего через operator.itemgetter — это быстрее и чище, чем лямбда:
from operator import itemgetter
# По зарплате, от большей к меньшей
by_salary = sorted(employees, key=itemgetter("salary"), reverse=True)
# По двум полям сразу: сначала отдел, внутри — имя
by_dept_name = sorted(employees, key=itemgetter("dept", "name"))sorted возвращает новый список и не трогает исходный. Если нужно отсортировать на месте — у списка есть метод .sort() с тем же аргументом key.
Тонкость: если у части словарей ключа нет, itemgetter упадёт с KeyError. Для таких данных берите лямбду с .get():
# Записи без salary уедут в конец со значением 0
by_salary = sorted(employees, key=lambda emp: emp.get("salary", 0))
Правило простое: пока все ключи гарантированно есть —itemgetter. Как только данные грязные — лямбда с.get(). Смешивать в одном проекте оба стиля без причины не стоит.
Фильтрация
Отбор записей по условию — тоже comprehension. Здесь ничего изобретать не нужно:
# Только отдел sales
sales = [emp for emp in employees if emp["dept"] == "sales"]
# Зарплата выше 100000
rich = [emp for emp in employees if emp.get("salary", 0) > 100000]Если нужен один первый подходящий элемент, а не весь список, не собирайте список ради первого элемента. Используйте генератор с next и значением по умолчанию:
# Первый сотрудник из it или None, если таких нет
first_it = next((emp for emp in employees if emp["dept"] == "it"), None)Без второго аргумента next бросит StopIteration, когда совпадений нет. Это частый источник падений в проде — всегда указывайте значение по умолчанию, если не уверены, что элемент точно есть.
Индекс по ключу
Искать сотрудника по id перебором списка — O(n) на каждый поиск. Если поисков много, один раз постройте словарь-индекс:
# id -> сам словарь сотрудника
by_id = {emp["id"]: emp for emp in employees}
by_id[2] # {'id': 2, 'name': 'Вера', ...} — теперь O(1)Важно: если значения ключа повторяются, при построении такого словаря останется последняя запись — предыдущие перезапишутся молча. Для неуникальных ключей это уже группировка, а не индекс.
Группировка по полю
Сгруппировать сотрудников по отделу можно двумя способами. Первый — collections.defaultdict, он работает с любым порядком записей:
from collections import defaultdict
groups = defaultdict(list)
for emp in employees:
groups[emp["dept"]].append(emp)
# groups["sales"] -> [Анна, Вера]
# groups["it"] -> [Борис, Глеб]Второй способ — itertools.groupby. У него есть коварная особенность: он группирует только подряд идущие элементы с одинаковым ключом. Перед ним данные обязательно нужно отсортировать по тому же ключу, иначе одна группа развалится на несколько.
from itertools import groupby
from operator import itemgetter
key = itemgetter("dept")
# Без sorted результат будет неверным
for dept, members in groupby(sorted(employees, key=key), key=key):
print(dept, [m["name"] for m in members])Для большинства задач defaultdict надёжнее и понятнее. groupby берите, когда данные уже отсортированы и вы работаете с потоком, который не хотите держать в памяти целиком.
Что выбрать под задачу
| Задача | Инструмент | Сложность |
|---|---|---|
| Значения одного поля | List comprehension | O(n) |
| Найти один элемент | next(генератор, None) | O(n) |
| Частый поиск по ключу | Словарь-индекс | O(1) на поиск |
| Группировка, любой порядок | defaultdict(list) | O(n) |
| Группировка отсортированного потока | itertools.groupby | O(n) |
Разбить на пачки
Когда список нужно отправить в API порциями по N записей, в Python 3.12 появилась itertools.batched:
# Только Python 3.12 и новее
from itertools import batched
for chunk in batched(employees, 2):
send_to_api(chunk) # chunk — кортеж из 2 словарейНа 3.11 и старше этой функции нет — нарежьте вручную срезами:
# Работает на любой версии Python 3
def batched_manual(items, size):
for start in range(0, len(items), size):
yield items[start:start + size]
Что дальше
Если списки словарей превращаются в постоянную боль с фильтрами и агрегациями — посмотрите в сторону pandas: pandas.DataFrame(employees) превратит тот же список в таблицу, где сортировка и группировка становятся одной строкой. Для валидации структуры входящих данных пригодится pydantic. А ближайший следующий шаг — почитайте раздел itertools в официальной документации: половина операций над списками словарей уже написана за вас.
Поиск источников: Реальные статьи и книги со ссылками, библиография по ГОСТ
Спросить за 15 ₽Источники: itertools — Functions creating iterators (Python docs), operator.itemgetter (Python docs)
Частые вопросы
Что делать, если у меня Python 3.11, а в примере используется batched?
Почему sorted с itemgetter падает с KeyError?
Почему groupby разбивает мою группу на несколько кусков?
Как безопасно найти первый подходящий элемент?
Индекс по ключу через словарь-comprehension теряет часть данных, почему?
Что быстрее: list comprehension или map с filter?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.