Словари Python
Словари Python: создание, доступ, обновление, удаление, перебор и генераторы — с примерами и разбором типичных ошибок.
Словарь Python — это изменяемая упорядоченная коллекция пар ключ-значение. В отличие от списков или кортежей (которые используют целочисленные позиции), словарь позволяет пометить каждое значение понятным ключом — это делает поиск быстрым, а код — более читаемым. Словари — один из наиболее используемых встроенных типов Python и основа для работы с JSON, управления конфигурацией, подсчёта слов и многого другого.
В этой главе рассмотрено всё необходимое для уверенной работы со словарями: создание, доступ к элементам, изменение, удаление, перебор, генераторы и типичные ошибки.
Связанные главы: Методы словарей | Вложенные словари | Перебор словарей | Копирование словарей | Генераторы Python
Что такое словарь?
Словарь сопоставляет уникальные хешируемые ключи со значениями любого типа. Главное правило: ключи должны быть неизменяемыми — строки, числа и кортежи из неизменяемых элементов подходят; списки и другие словари — нет.
# string keys → integer values
inventory = {'apple': 10, 'banana': 5, 'orange': 8}
# mixed value types are fine
person = {'name': 'Alice', 'age': 30, 'active': True}
# tuple keys work because tuples are immutable
grid = {(0, 0): 'origin', (1, 0): 'east', (0, 1): 'north'}Начиная с Python 3.7 словари сохраняют порядок вставки — при переборе ключи всегда возвращаются в том порядке, в котором были добавлены.
Создание словаря
Литерал в фигурных скобках
Самый распространённый способ: пары ключ-значение разделяются запятыми, между ключом и значением ставится двоеточие, весь литерал заключается в {}.
config = {'host': 'localhost', 'port': 5432, 'debug': True}
print(config)
# {'host': 'localhost', 'port': 5432, 'debug': True}Пустой словарь — просто {}.
empty = {}
print(type(empty)) # <class 'dict'>Конструктор dict()
Передайте именованные аргументы в dict(), когда ключи являются допустимыми идентификаторами Python:
config = dict(host='localhost', port=5432, debug=True)
print(config)
# {'host': 'localhost', 'port': 5432, 'debug': True}Можно также передать итерируемый объект из двухэлементных последовательностей:
pairs = [('x', 10), ('y', 20)]
point = dict(pairs)
print(point) # {'x': 10, 'y': 20}dict.fromkeys()
Создание словаря из списка ключей, которым присваивается одно общее начальное значение:
defaults = dict.fromkeys(['timeout', 'retries', 'verbose'], 0)
print(defaults)
# {'timeout': 0, 'retries': 0, 'verbose': 0}Важно: если значением по умолчанию является изменяемый объект (например, список), все ключи будут ссылаться на один и тот же объект. Вместо этого используйте генератор словаря (см. ниже).
Генератор словаря
Лаконичный способ построить словарь из любого итерируемого объекта:
squares = {x: x ** 2 for x in range(1, 6)}
print(squares)
# {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}Полный синтаксис см. в Генераторы Python.
Доступ к значениям
Доступ через квадратные скобки
inventory = {'apple': 10, 'banana': 5, 'orange': 8}
print(inventory['apple']) # 10Если ключ не существует, Python вызывает KeyError:
try:
print(inventory['grape'])
except KeyError as e:
print(f'KeyError: {e}') # KeyError: 'grape'get() — безопасный доступ со значением по умолчанию
dict.get(key, default) возвращает значение, если ключ существует, или default (по умолчанию None) — без вызова исключения.
print(inventory.get('grape')) # None
print(inventory.get('grape', 0)) # 0
print(inventory.get('apple', 0)) # 10Используйте .get(), когда не уверены в наличии ключа. Используйте доступ через квадратные скобки, когда ключ обязан быть — отсутствие ключа в этом случае является настоящей ошибкой, которая должна проявляться как исключение.
Проверка существования ключа
print('apple' in inventory) # True
print('grape' in inventory) # False
print('grape' not in inventory) # Truein проверяет ключи, а не значения. Работает за O(1) в среднем — постоянное время, независимо от размера словаря.
Добавление и обновление элементов
Добавление новой пары ключ-значение
Присвоение значения несуществующему ключу:
inventory['grape'] = 12
print(inventory)
# {'apple': 10, 'banana': 5, 'orange': 8, 'grape': 12}Обновление существующего значения
Присвоение значения уже существующему ключу — старое значение заменяется:
inventory['apple'] = 15
print(inventory['apple']) # 15update() — массовое добавление или перезапись
Передайте другой словарь или итерируемый объект с парами ключ-значение:
inventory.update({'banana': 7, 'mango': 3})
print(inventory)
# {'apple': 15, 'banana': 7, 'orange': 8, 'grape': 12, 'mango': 3}Слияние с | (Python 3.9+)
Оператор | создаёт новый объединённый словарь. Если ключ присутствует в обоих, побеждает правый операнд:
a = {'x': 1, 'y': 2}
b = {'y': 99, 'z': 3}
merged = a | b
print(merged) # {'x': 1, 'y': 99, 'z': 3}Используйте |=, чтобы слить b в a на месте — аналог a.update(b), но нагляднее.
setdefault() — вставка только при отсутствии ключа
dict.setdefault(key, default) вставляет ключ со значением по умолчанию, если его нет, а затем всегда возвращает значение для этого ключа. Это эффективнее конструкции if key not in d: d[key] = default.
scores = {'Alice': 95}
scores.setdefault('Bob', 0) # Bob absent → inserts 0, returns 0
scores.setdefault('Alice', 0) # Alice present → does nothing, returns 95
print(scores) # {'Alice': 95, 'Bob': 0}Классический пример использования — группировка элементов:
words = ['cat', 'car', 'bus', 'can', 'bat']
groups = {}
for word in words:
groups.setdefault(word[0], []).append(word)
print(groups)
# {'c': ['cat', 'car', 'can'], 'b': ['bus', 'bat']}Удаление элементов
del — удаление по ключу
inventory = {'apple': 15, 'banana': 7, 'orange': 8, 'grape': 12}
del inventory['orange']
print(inventory)
# {'apple': 15, 'banana': 7, 'grape': 12}del вызывает KeyError, если ключ отсутствует. Чтобы этого избежать, сначала проверьте через in или используйте pop() со значением по умолчанию.
pop() — удаление с возвратом значения
removed = inventory.pop('grape')
print(removed) # 12
print(inventory) # {'apple': 15, 'banana': 7}Передайте второй аргумент, чтобы избежать KeyError при возможном отсутствии ключа:
val = inventory.pop('pear', 'not found')
print(val) # not foundpopitem() — удаление последнего добавленного элемента (Python 3.7+)
d = {'a': 1, 'b': 2, 'c': 3}
last = d.popitem()
print(last) # ('c', 3)
print(d) # {'a': 1, 'b': 2}clear() — удаление всех элементов
d = {'a': 1, 'b': 2}
d.clear()
print(d) # {}Перебор словаря
Три представления позволяют перебирать разные части словаря. Все три являются живыми — они отражают изменения в словаре без необходимости пересоздания.
Ключи (итерация по умолчанию)
Прямой перебор словаря возвращает его ключи:
person = {'name': 'Alice', 'age': 30, 'city': 'Paris'}
for key in person:
print(key)
# name
# age
# cityperson.keys() возвращает те же ключи в виде явного объекта-представления dict_keys.
Значения
for val in person.values():
print(val)
# Alice
# 30
# ParisПары ключ-значение
for key, val in person.items():
print(f'{key}: {val}')
# name: Alice
# age: 30
# city: Paris.items() — наиболее распространённый выбор, когда внутри цикла нужны и ключ, и значение. Дополнительные шаблоны см. в Перебор словарей.
Представления являются живыми
d = {'a': 1}
keys_view = d.keys()
d['b'] = 2
print(keys_view) # dict_keys(['a', 'b']) ← reflects the new keyГенераторы словарей
Генераторы словарей позволяют строить или преобразовывать словари в одном читаемом выражении.
Фильтрация по значению
inventory = {'apple': 15, 'banana': 7, 'orange': 8, 'grape': 12, 'mango': 3}
popular = {k: v for k, v in inventory.items() if v >= 8}
print(popular)
# {'apple': 15, 'orange': 8, 'grape': 12}Инвертирование словаря (замена ключей и значений)
original = {'a': 1, 'b': 2, 'c': 3}
swapped = {v: k for k, v in original.items()}
print(swapped) # {1: 'a', 2: 'b', 3: 'c'}Это работает корректно только при условии уникальности всех значений.
Счётчик частоты слов
sentence = 'the quick brown fox jumps over the lazy dog'
freq = {}
for word in sentence.split():
freq[word] = freq.get(word, 0) + 1
print(freq)
# {'the': 2, 'quick': 1, 'brown': 1, 'fox': 1, ...}Полезные встроенные функции
| Операция | Пример | Результат |
|---|---|---|
| Длина | len(inventory) | количество пар ключ-значение |
| Копия (поверхностная) | inventory.copy() | новый словарь с теми же ссылками |
| Преобразовать в список ключей | list(inventory) | ['apple', 'banana', ...] |
| Отсортированные ключи | sorted(inventory) | ['apple', 'banana', ...] по алфавиту |
| Все значения истинны? | all(inventory.values()) | True / False |
| Хотя бы одно значение истинно? | any(inventory.values()) | True / False |
Полный справочник по всем методам словаря (copy, fromkeys, update, setdefault, popitem и другим) см. в Методы словарей Python.
Типичные ошибки
1. KeyError при отсутствующем ключе — всегда используйте .get() или in, если ключ может отсутствовать.
2. Нехешируемые типы ключей — списки не могут быть ключами словаря, поскольку они изменяемы. Используйте кортеж:
# This raises TypeError: unhashable type: 'list'
# bad = {[1, 2]: 'value'}
# Use a tuple:
coords = {(1, 2): 'A', (3, 4): 'B'}
print(coords[(1, 2)]) # A3. Изменение во время перебора — добавление или удаление ключей во время цикла по словарю вызывает RuntimeError. Сначала сделайте снимок:
d = {'a': 1, 'b': 2, 'c': 3}
for key in list(d): # list() copies the keys
if d[key] < 2:
del d[key]
print(d) # {'b': 2, 'c': 3}4. Поверхностная копия против глубокой — dict.copy() и {**d} выполняют поверхностное копирование. Вложенные изменяемые значения (списки, словари) по-прежнему остаются общими. Используйте copy.deepcopy(), когда нужны полностью независимые копии. См. Копирование словарей.
Словари против других типов коллекций
| Характеристика | dict | list | tuple | set |
|---|---|---|---|---|
| Упорядочен | Да (3.7+) | Да | Да | Нет |
| Изменяемый | Да | Да | Нет | Да |
| Индексируется по | Ключу | Целому числу | Целому числу | — |
| Дубликаты | Ключи: нет; Значения: да | Да | Да | Нет |
| Основное применение | Сопоставление ключ-значение | Упорядоченная последовательность | Неизменяемая последовательность | Уникальность / операции над множествами |
Когда нужно найти что-то по осмысленному метке, а не по позиции, словарь — почти всегда правильный выбор. Для упорядоченных уникальных значений рассмотрите множество. Для простой упорядоченной последовательности используйте список.