Вложенные словари
Узнайте, как создавать, читать, изменять, удалять и перебирать вложенные словари в Python с примерами и лучшими практиками.
Вложенный словарь — это словарь, значениями которого являются другие словари. Такая структура образует иерархию (дерево данных), идеально подходящую для представления сгруппированных данных реального мира: коллекций профилей пользователей, конфигурационных файлов или ответов JSON API.
В этой главе рассматриваются:
- Создание вложенных словарей
- Доступ к значениям на любом уровне глубины
- Изменение и добавление записей
- Удаление ключей и вложенных словарей
- Перебор вложенных словарей
- Использование
.get()для избежанияKeyError - Работа с глубоко вложенными данными и JSON
Создание вложенных словарей
Определите словарь, значениями которого являются другие словари:
people = {
"person1": {"name": "Alice", "age": 30},
"person2": {"name": "Bob", "age": 25},
}
print(people)
# {'person1': {'name': 'Alice', 'age': 30}, 'person2': {'name': 'Bob', 'age': 25}}Вложенный словарь можно также строить постепенно:
people = {}
people["person1"] = {"name": "Alice", "age": 30}
people["person2"] = {"name": "Bob", "age": 25}
print(people["person1"]) # {'name': 'Alice', 'age': 30}Глубина вложенности не ограничена — внутреннее значение само может быть вложенным словарём.
Доступ к значениям вложенного словаря
Используйте цепочку квадратных скобок, чтобы обратиться к значению на любом уровне:
Безопасный доступ с помощью .get()
Цепочка квадратных скобок вызывает KeyError, если ключ отсутствует. Используйте .get(), чтобы вернуть значение по умолчанию:
people = {
"person1": {"name": "Alice", "age": 30},
}
# Safe: returns None if "person3" does not exist
print(people.get("person3", {}).get("name", "Unknown")) # Unknown
# Risky: raises KeyError
# print(people["person3"]["name"])Паттерн .get(outer_key, {}).get(inner_key, default) — идиоматический способ читать необязательные вложенные данные без блока try/except.
Изменение значений вложенного словаря
Присваивайте значение напрямую через цепочку ключей:
Добавление новых вложенных словарей
Присвойте новый словарный литерал новому внешнему ключу:
Использование setdefault() для добавления только при отсутствии ключа
setdefault() вставляет ключ со значением по умолчанию только если ключ ещё не существует — полезно при построении вложенных словарей из потока данных:
scores = {}
for student, subject, grade in [
("Alice", "math", 90),
("Alice", "english", 85),
("Bob", "math", 78),
]:
scores.setdefault(student, {})[subject] = grade
print(scores)
# {'Alice': {'math': 90, 'english': 85}, 'Bob': {'math': 78}}Удаление ключей и вложенных словарей
Используйте del для удаления ключа (и его значения) на любом уровне:
people = {
"person1": {"name": "Alice", "age": 30},
"person2": {"name": "Bob", "age": 25},
"person3": {"name": "Carol", "age": 40},
}
del people["person2"]["age"] # remove one key from an inner dict
del people["person3"] # remove an entire sub-dictionary
print(people)
# {'person1': {'name': 'Alice', 'age': 30}, 'person2': {'name': 'Bob'}}Используйте .pop(), если нужно также получить удалённое значение:
removed = people["person1"].pop("age", None)
print(removed) # 30
print(people["person1"]) # {'name': 'Alice'}Перебор вложенных словарей
Цикл по внешним ключам и внутренним элементам
people = {
"person1": {"name": "Alice", "age": 30},
"person2": {"name": "Bob", "age": 25},
}
for person_id, details in people.items():
print(f"{person_id}:")
for key, value in details.items():
print(f" {key}: {value}")Вывод:
person1:
name: Alice
age: 30
person2:
name: Bob
age: 25Преобразование вложенного словаря в список записей
Распространённый паттерн при подготовке данных к обработке:
people = {
"person1": {"name": "Alice", "age": 30},
"person2": {"name": "Bob", "age": 25},
}
records = [
{"id": pid, **info}
for pid, info in people.items()
]
print(records)
# [{'id': 'person1', 'name': 'Alice', 'age': 30},
# {'id': 'person2', 'name': 'Bob', 'age': 25}]Глубоко вложенные словари
Python не накладывает ограничений на глубину вложенности. Вот пример трёхуровневой структуры, представляющей подразделения компании:
company = {
"engineering": {
"frontend": {
"lead": "Alice",
"headcount": 5,
},
"backend": {
"lead": "Bob",
"headcount": 8,
},
},
"marketing": {
"content": {
"lead": "Carol",
"headcount": 3,
},
},
}
# Access three levels deep
print(company["engineering"]["backend"]["lead"]) # Bob
# Iterate two levels and collect leads
leads = [
dept_data["lead"]
for dept_data in (
team
for teams in company.values()
for team in teams.values()
)
]
print(leads) # ['Alice', 'Bob', 'Carol']При чтении глубоких ключей, которые могут отсутствовать, используйте цепочку вызовов .get():
lead = company.get("hr", {}).get("recruitment", {}).get("lead", "Not assigned")
print(lead) # Not assignedВложенные словари и JSON
Объекты JSON напрямую отображаются на вложенные словари Python. Модуль json выполняет преобразование между ними:
import json
data = {
"users": {
"u1": {"name": "Alice", "active": True},
"u2": {"name": "Bob", "active": False},
}
}
# Serialize to JSON string
json_str = json.dumps(data, indent=2)
print(json_str)
# Deserialize back to a nested dict
restored = json.loads(json_str)
print(restored["users"]["u1"]["name"]) # AliceЭто типичный рабочий процесс при работе с REST API или чтении конфигурационных файлов.
Распространённые ошибки
Ловушка разделяемых ссылок. Если скопировать внутренний словарь по ссылке и затем изменить его, изменится и оригинал, и копия:
original = {"a": {"x": 1}}
shallow = original.copy() # copies only the outer dict
shallow["a"]["x"] = 99
print(original["a"]["x"]) # 99 ← original is affected!Используйте copy.deepcopy(), когда нужна полностью независимая копия:
import copy
original = {"a": {"x": 1}}
deep = copy.deepcopy(original)
deep["a"]["x"] = 99
print(original["a"]["x"]) # 1 ← original is safeСмотрите главу Копирование словарей для полного сравнения поверхностного и глубокого копирования.
KeyError при отсутствующих промежуточных ключах. Запись d["a"]["b"] = 1 вызывает KeyError, если "a" ещё не существует. Используйте setdefault или collections.defaultdict для автоматического создания промежуточных уровней.
Краткий справочник
| Задача | Синтаксис |
|---|---|
| Доступ к внутреннему значению | d["outer"]["inner"] |
| Безопасный доступ | d.get("outer", {}).get("inner", default) |
| Добавить / обновить внутренний ключ | d["outer"]["inner"] = value |
| Добавить новый вложенный словарь | d["new_key"] = {...} |
| Удалить внутренний ключ | del d["outer"]["inner"] |
| Удалить вложенный словарь | del d["outer"] |
| Перебрать все элементы | for k, v in d.items(): for ik, iv in v.items(): |
| Глубокое копирование | import copy; copy.deepcopy(d) |