Словари (dict) в Python — полный гайд для аналитика
Коротко
Словарь (dict) — основная структура данных для хранения пар «ключ — значение» в Python. Аналитики используют словари постоянно: маппинг кодов в названия, подсчёт вхождений, хранение конфигов и параметров запросов. На собеседованиях спрашивают про разницу [] и .get(), мутабельность ключей и сложность операций.
Создание словаря
# Литерал — самый частый способ
user = {'name': 'Анна', 'age': 25, 'city': 'Москва'}
# Конструктор dict()
user = dict(name='Анна', age=25, city='Москва')
# Из списка кортежей
pairs = [('name', 'Анна'), ('age', 25)]
user = dict(pairs)
# dict.fromkeys() — все значения одинаковые
metrics = dict.fromkeys(['dau', 'wau', 'mau'], 0)
# {'dau': 0, 'wau': 0, 'mau': 0}
# Пустой словарь
empty = {}Ключи должны быть хешируемыми: строки, числа, tuple. Списки и другие dict ключами быть не могут.
Доступ к элементам: [] vs .get()
config = {'host': 'localhost', 'port': 5432, 'db': 'analytics'}
# Квадратные скобки — бросает KeyError, если ключа нет
config['host'] # 'localhost'
config['password'] # KeyError: 'password'
# .get() — возвращает None (или значение по умолчанию)
config.get('host') # 'localhost'
config.get('password') # None
config.get('password', 'secret') # 'secret'Правило: используйте [], когда ключ точно есть (или хотите явную ошибку). .get() — когда ключа может не быть.
Добавление и обновление
user = {'name': 'Анна', 'age': 25}
# Добавить / перезаписать по ключу
user['city'] = 'Москва'
user['age'] = 26
# update() — добавить/обновить сразу несколько
user.update({'age': 27, 'role': 'analyst'})
# В Python 3.9+ можно через оператор |
merged = user | {'company': 'Яндекс', 'level': 'middle'}Удаление
data = {'a': 1, 'b': 2, 'c': 3}
# del — удаляет по ключу, KeyError если нет
del data['a']
# pop() — удаляет и возвращает значение
val = data.pop('b') # val = 2
val = data.pop('z', None) # None, без KeyError
# clear() — очистить весь словарь
data.clear() # {}Методы keys, values, items
params = {'metric': 'revenue', 'period': 'month', 'segment': 'new'}
list(params.keys()) # ['metric', 'period', 'segment']
list(params.values()) # ['revenue', 'month', 'new']
list(params.items()) # [('metric', 'revenue'), ('period', 'month'), ('segment', 'new')]
# Проверка наличия ключа
'metric' in params # True
'channel' in params # Falsekeys(), values(), items() возвращают view-объекты — они обновляются при изменении словаря.
Итерирование
scores = {'sql': 85, 'python': 92, 'stats': 78}
# По ключам (по умолчанию)
for key in scores:
print(key)
# По парам ключ-значение
for key, value in scores.items():
print(f'{key}: {value}')
# По значениям
for value in scores.values():
print(value)Порядок итерации совпадает с порядком вставки (гарантировано с Python 3.7+).
Dict comprehension
# Базовый синтаксис
{ключ: значение for элемент in итерируемый_объект}
# Квадраты чисел
{x: x ** 2 for x in range(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# С фильтрацией
prices = {'яблоко': 80, 'манго': 350, 'банан': 60, 'авокадо': 290}
cheap = {k: v for k, v in prices.items() if v < 200}
# {'яблоко': 80, 'банан': 60}
# Инвертировать словарь
original = {'MSK': 'Москва', 'SPB': 'Петербург'}
inverted = {v: k for k, v in original.items()}
# {'Москва': 'MSK', 'Петербург': 'SPB'}Подробнее про синтаксис comprehensions — в статье про list comprehension.
Вложенные словари
users = {
101: {'name': 'Анна', 'scores': {'sql': 85, 'python': 92}},
102: {'name': 'Борис', 'scores': {'sql': 78, 'python': 88}},
}
# Доступ
users[101]['name'] # 'Анна'
users[101]['scores']['sql'] # 85
# Безопасный доступ к вложенным ключам
users.get(999, {}).get('name') # None вместо KeyErrordefaultdict — словарь с значением по умолчанию
from collections import defaultdict
# Подсчёт вхождений
word_count = defaultdict(int)
for word in ['sql', 'python', 'sql', 'sql', 'python']:
word_count[word] += 1
# defaultdict(int, {'sql': 3, 'python': 2})
# Группировка
by_city = defaultdict(list)
for name, city in [('Анна', 'Москва'), ('Борис', 'Москва'), ('Вика', 'СПб')]:
by_city[city].append(name)
# {'Москва': ['Анна', 'Борис'], 'СПб': ['Вика']}defaultdict автоматически создаёт значение при обращении к несуществующему ключу — не нужны проверки и setdefault().
Примеры для аналитика
# Маппинг кодов в названия
status_map = {0: 'новый', 1: 'активный', 2: 'отток'}
user_status = status_map.get(row['status_code'], 'неизвестно')
# Подсчёт событий
from collections import Counter
events = ['page_view', 'click', 'page_view', 'purchase', 'click', 'click']
Counter(events)
# Counter({'click': 3, 'page_view': 2, 'purchase': 1})
# Параметры конфигурации
query_params = {
'date_from': '2026-01-01',
'date_to': '2026-03-31',
'metrics': ['revenue', 'orders'],
'segment': 'organic',
}Частые ошибки
KeyError вместо .get(). Забыли, что ключа может не быть — получили необработанное исключение. Привычка использовать .get() с дефолтом спасает.
Мутабельный объект как значение по умолчанию. Классический баг:
# Опасно — один и тот же список для всех вызовов
def add_tag(tag, tags={}):
tags[tag] = True
return tags
add_tag('sql') # {'sql': True}
add_tag('python') # {'sql': True, 'python': True} — сюрприз!
# Правильно
def add_tag(tag, tags=None):
if tags is None:
tags = {}
tags[tag] = True
return tagsИзменение словаря во время итерации. RuntimeError: dictionary changed size during iteration — нельзя добавлять/удалять ключи в цикле. Решение: итерировать по копии list(d.keys()).
Вопросы с собеседований
— Чем dict[key] отличается от dict.get(key)?
— [] бросает KeyError, если ключа нет. .get() возвращает None или указанное значение по умолчанию. Используйте [], когда ключ обязан существовать, .get() — когда может отсутствовать.
— Какие объекты могут быть ключами словаря? — Только хешируемые (immutable): str, int, float, tuple (если все элементы хешируемы), frozenset. list и dict — нельзя, потому что они мутабельные и не имеют стабильного хеша.
— Какова сложность поиска по ключу в dict? — В среднем O(1) благодаря хеш-таблице. В худшем случае O(n) из-за коллизий, но на практике это крайне редко.
— Сохраняется ли порядок элементов в dict? — Да, с Python 3.7+ порядок вставки гарантирован спецификацией языка. В CPython 3.6 это было деталью реализации, но не гарантией.
— Чем dict отличается от defaultdict? — defaultdict автоматически создаёт значение при обращении к несуществующему ключу (через фабрику: int, list, set). Обычный dict бросает KeyError.
Потренировать Python-вопросы на практике можно в тренажёре Карьерника. Подробнее про list comprehension и разницу list vs tuple. Больше примеров вопросов — на отдельной странице.
Открыть тренажёр в Telegram — вопросы по Python, pandas, SQL и аналитике. Бесплатно.
FAQ
Когда использовать dict, а когда list?
dict — когда нужен быстрый доступ по ключу: маппинги, конфиги, кеши, подсчёт вхождений. list — когда важен порядок и индексация, а ключи не нужны. Если данные — пары «ключ — значение», это dict.
Чем dict отличается от JSON?
JSON — текстовый формат сериализации. dict — структура данных в памяти Python. json.loads() превращает JSON-строку в dict, json.dumps() — обратно. Ключи JSON — только строки, в dict ключом может быть любой хешируемый объект.
Можно ли сортировать словарь?
Сам dict не имеет метода sort. Но можно создать новый отсортированный словарь: dict(sorted(d.items(), key=lambda x: x[1])). Это создаёт новый dict с элементами в нужном порядке.
Что лучше: несколько if-проверок или dict-маппинг?
Dict-маппинг чище и быстрее. Вместо цепочки if status == 0: ... elif status == 1: ... лучше status_map = {0: 'новый', 1: 'активный'} и status_map.get(status, 'неизвестно'). Особенно при большом количестве вариантов.