W3docs

Вложенные словари

Узнайте, как создавать, читать, изменять, удалять и перебирать вложенные словари в 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}

Глубина вложенности не ограничена — внутреннее значение само может быть вложенным словарём.

Доступ к значениям вложенного словаря

Используйте цепочку квадратных скобок, чтобы обратиться к значению на любом уровне:

python— editable, runs on the server

Безопасный доступ с помощью .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.

Изменение значений вложенного словаря

Присваивайте значение напрямую через цепочку ключей:

python— editable, runs on the server

Добавление новых вложенных словарей

Присвойте новый словарный литерал новому внешнему ключу:

python— editable, runs on the server

Использование 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)

Практика

Практика
Which method safely reads a missing key from a nested dict without raising a KeyError?
Which method safely reads a missing key from a nested dict without raising a KeyError?
Was this page helpful?