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

Что вы получите и сколько это займёт
К концу этой статьи вы будете уверенно доставать данные из структуры вида [{"name": "Аня", "age": 30}, {"name": "Борис", "age": 25}]: фильтровать по условию, сортировать по полю, группировать и превращать в другую форму. Это самая частая структура, которую отдают JSON-ответы API, библиотека csv.DictReader и базы данных через драйверы. На всё уйдёт минут двадцать, если запускать примеры по ходу.
Примеры проверены на CPython 3.12, но всё, что здесь есть, работает начиная с Python 3.7 — с этой версии обычные словари гарантированно сохраняют порядок вставки ключей.
Что нужно до начала
Вам понадобится установленный Python 3.7 или новее и интерпретатор под рукой — терминал с командой python или python3, либо любой редактор, который умеет запускать файл. Никаких сторонних пакетов для базовой работы не нужно: list и dict встроены. Модули json, csv и operator тоже входят в стандартную библиотеку.
Проверьте версию:
python --version
# Должно вывести что-то вроде: Python 3.12.3Если версия ниже 3.7, часть примеров про порядок ключей будет вести себя иначе — обновитесь до актуальной ветки.
Откуда берутся списки словарей
Чаще всего вы не создаёте такую структуру руками, а получаете готовой. Три типичных источника:
- JSON-ответ API. Метод
json.loads(text)превращает JSON-массив объектов ровно в список словарей. - CSV-файл. Класс
csv.DictReaderотдаёт каждую строку файла как словарь, где ключи — заголовки колонок. - База данных. Многие драйверы (например,
psycopgс курсором-словарём илиsqlite3с настроеннымrow_factory) возвращают строки словарями.
Разберём один живой пример — чтение CSV:
import csv
# Файл people.csv с колонками name,age,city
with open("people.csv", encoding="utf-8", newline="") as f:
reader = csv.DictReader(f)
people = list(reader)
# people теперь список словарей:
# [{"name": "Аня", "age": "30", "city": "Москва"}, ...]
print(people[0]["name"])
# Ожидаем в консоли: АняЗдесь важна ловушка, на которую натыкаются все новички: DictReader отдаёт значения строками. Поле age будет "30", а не 30. Сравнивать и сортировать по нему как по числу без явного int() не выйдет — вернётесь к этому в разделе про сортировку.
Основные операции
Достать одно поле из каждого элемента
Спискового включения (list comprehension) достаточно для 90% случаев. Оно короче и быстрее ручного цикла с append:
names = [person["name"] for person in people]
# ['Аня', 'Борис', ...]Если ключа может не быть в части словарей, person["name"] выбросит KeyError. Используйте person.get("name") — он вернёт None вместо падения, либо значение по умолчанию вторым аргументом: person.get("name", "без имени").
Отфильтровать по условию
Условие пишется прямо в включении после if:
adults = [p for p in people if int(p["age"]) >= 18]Обратите внимание на int(p["age"]) — если данные пришли из CSV строками, без преобразования сравнение "5" >= 18 упадёт с TypeError.
Отсортировать по полю
Функция sorted с параметром key. Ключом удобно давать operator.itemgetter — он читается лучше лямбды и работает чуть быстрее:
from operator import itemgetter
by_age = sorted(people, key=itemgetter("age"))Но и тут строки из CSV подведут: "100" окажется «меньше» "9", потому что сравниваются символы, а не числа. Приводите тип в лямбде:
by_age = sorted(people, key=lambda p: int(p["age"]))
# Обратный порядок — параметром reverse
by_age_desc = sorted(people, key=lambda p: int(p["age"]), reverse=True)sorted возвращает новый список и не трогает исходный. Метод people.sort(...) сортирует на месте и возвращает None — не присваивайте его результат переменной, это частая ошибка.
Сгруппировать по ключу
Здесь есть подводный камень. Функция itertools.groupby группирует только подряд идущие элементы, поэтому список сначала надо отсортировать по тому же ключу — иначе группы разорвутся. Для большинства задач надёжнее и понятнее collections.defaultdict:
from collections import defaultdict
by_city = defaultdict(list)
for person in people:
by_city[person["city"]].append(person)
# by_city["Москва"] — список всех москвичей
for city, group in by_city.items():
print(city, len(group))Где это ломается
Три ошибки, которые вы почти наверняка встретите:
- Изменение списка во время итерации. Если удалять элементы из списка прямо в цикле
forпо нему, часть элементов проскочит без обработки. Соберите новый список включением с условием вместо удаления из старого. - Общий вложенный объект. Приём
[shared_dict] * 3создаёт список из трёх ссылок на один и тот же словарь. Меняете один — меняются все три. Создавайте каждый словарь отдельно. - Строки вместо чисел. Уже упомянутая ловушка
DictReaderи любых текстовых источников. Приводите тип на входе, а не в каждой операции.
Список словарей — это таблица без схемы: колонки у строк могут не совпадать, а типы никто не проверяет. Половина багов здесь — про поле, которое есть не у всех, или про число, которое на самом деле строка.
Быстрая шпаргалка операций
| Задача | Инструмент | Возвращает |
|---|---|---|
| Достать одно поле из всех | [p["k"] for p in data] | Новый список значений |
| Фильтр по условию | [p for p in data if ...] | Новый список словарей |
| Сортировка (новый список) | sorted(data, key=...) | Новый список |
| Сортировка на месте | data.sort(key=...) | None |
| Группировка по полю | defaultdict(list) | Словарь списков |
| Поиск первого совпадения | next((p for p in data if ...), None) | Словарь или None |
Если не получилось
KeyError: 'name'— вы обратились к ключу, которого нет хотя бы в одном словаре. Заменитеp["name"]наp.get("name").TypeError: '>=' not supported between instances of 'str' and 'int'— сравниваете строку с числом. Оберните значение вint()илиfloat().TypeError: '<' not supported between instances of 'dict' and 'dict'— вызвалиsorted(data)безkey. Словари напрямую не сравниваются, укажите поле:sorted(data, key=itemgetter("age")).- Сортировка «съела» результат — присвоили итог
data.sort()переменной и получилиNone. Либо не присваивайте, либо используйтеsorted.
Что дальше
Когда базовые операции перестанут вызывать вопросы, посмотрите в сторону двух вещей. Первая — dataclasses из стандартной библиотеки: если структура словарей стабильна, класс с типизированными полями защитит от опечаток в ключах и лишних get. Вторая — библиотека pandas: когда списков словарей становятся десятки тысяч и нужны агрегации по нескольким полям сразу, pandas.DataFrame.from_records(data) избавит от ручных циклов. Порог перехода — не размер данных, а количество разных операций над одним и тем же набором: две-три группировки и сводки подряд — сигнал, что пора.
Шеф-повар «из холодильника»: Рецепты по фото или списку остатков продуктов.
Спросить за 8 ₽Источники: Python: Data Structures — List Comprehensions (официальная документация), Python: Sorting Techniques (HOWTO)
Частые вопросы
У меня Python 3.6 или старше, примеры сработают?
Почему сортировка по числу выдаёт неправильный порядок?
Как найти один словарь по условию, а не весь список?
Не сработала группировка через groupby — группы разорвались, в чём дело?
Стоит ли сразу брать pandas вместо списков словарей?
Есть ли подводные камни в проде при работе с большими списками словарей?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.