Как только задача становится хоть немного похожей на реальность, «чистый список» перестаёт хватать. В мире есть товары, у товаров есть свойства, у заказов есть позиции, у позиций — количество. Это уже не плоские данные, а структура из структур.
Разберём это на одном примере: модель каталога товаров и заказов в памяти.
Мы:
- сначала соберём рабочую модель и сделаем полезную операцию;
- потом разложим по полочкам, как устроена вложенность и как к ней обращаться;
- затем улучшим модель и расширим возможности.
Один пример на всю статью.

Сначала пишем код
Есть маленький каталог и список заказов. Нужна программа, которая печатает чек по заказу: что купили и на какую сумму.
Начнём с простого представления:
- каталог — словарь, где ключ это id товара, а значение — словарь свойств товара;
- заказ — словарь с id заказа и списком купленных позиций;
- позиция — пара: product_id и qty.
catalog = {
"p100": {"name": "Keyboard", "price": 49.9, "category": "electronics"},
"p200": {"name": "Mouse", "price": 19.9, "category": "electronics"},
"p300": {"name": "Notebook", "price": 3.5, "category": "stationery"},
}
orders = [
{
"order_id": "o9001",
"items": [
{"product_id": "p100", "qty": 1},
{"product_id": "p300", "qty": 4},
]
},
{
"order_id": "o9002",
"items": [
{"product_id": "p200", "qty": 2},
]
}
]
Теперь печатаем чек для первого заказа.
order = orders[0]
total = 0
print("Order:", order["order_id"])
for item in order["items"]:
product = catalog[item["product_id"]]
line_sum = product["price"] * item["qty"]
total += line_sum
print(product["name"], "x", item["qty"], "=", round(line_sum, 2))
print("Total:", round(total, 2))
Эта версия уже делает полезную вещь: по данным в памяти строит чек.
Теперь понятно, зачем вообще нужна вложенность.
Разбираем, как это устроено
Почему каталог — словарь словарей
catalog = {
"p100": {"name": "...", "price": ..., "category": "..."},
...
}
Смысл:
- внешний словарь даёт быстрый доступ по product_id;
- внутренний словарь хранит свойства товара по именам.
Если бы каталог был списком, поиск товара по id выглядел бы как цикл с перебором. А словарь позволяет получить товар сразу:
product = catalog[item["product_id"]]
То есть у нас dict для индекса, а внутри снова dict для удобного доступа к полям.
Почему заказ — это словарь со списком позиций
{
"order_id": "o9001",
"items": [ ... ]
}
items — список, потому что:
- порядок позиций может быть важен;
- позиций может быть сколько угодно;
- мы хотим сходу пройтись циклом по всем позициям.
Почему позиция — словарь
{"product_id": "p100", "qty": 1}
Это «микро-объект» внутри списка.
Словарь здесь выигрывает читаемостью: item["qty"] сразу понятно, что это количество. Если бы это был список [product_id, qty], пришлось бы помнить индексы.
Как читать вложенные структуры
В нашем чеке есть цепочка доступа:
- получаем заказ из списка:
order = orders[0] - достаём список позиций:
for item in order["items"]: - по product_id находим товар в каталоге:
product = catalog[item["product_id"]] - достаём поля товара и позиции:
product["price"], product["name"], item["qty"]
Это и есть нормальный способ работы с «структурами реального мира»:
список контейнеров → контейнеры внутри контейнеров → доступ по ключам.
Улучшаем и расширяем
Функции разберем в следующей главе.
Сейчас наш код для чека — голая логика «на месте». Сделаем две вещи:
- вынесем создание чека в функцию;
- добавим полезные операции поверх той же модели данных.
Чек как функция
def print_receipt(order, catalog):
total = 0
print("Order:", order["order_id"])
for item in order["items"]:
product = catalog[item["product_id"]]
line_sum = product["price"] * item["qty"]
total += line_sum
print(product["name"], "x", item["qty"], "=", round(line_sum, 2))
print("Total:", round(total, 2))
return total
📢 Подписывайтесь на наш Telegram-канал.
Там вы найдете анонсы обучающих статей и видео, готовый код для ваших проектов и увлекательные курсы. Ничего лишнего — только практика, вдохновение и развитие.
Использование:
print_receipt(orders[0], catalog)
Модель данных не изменилась. Мы просто сделали работу с ней чище.
Операция «общая выручка»
Раз данные уже структурированы, можно легко считать:
def total_revenue(orders, catalog):
revenue = 0
for order in orders:
for item in order["items"]:
product = catalog[item["product_id"]]
revenue += product["price"] * item["qty"]
return revenue
print("Revenue:", round(total_revenue(orders, catalog), 2))
Снова один и тот же доступ к вложенным структурам, только цель другая.
Операция «товары по категориям»
Хотим получить все товары одной категории, например электронику.
def products_by_category(catalog, category):
result = []
for product_id, product in catalog.items():
if product["category"] == category:
result.append(product_id)
return result
print(products_by_category(catalog, "electronics"))
Две важные детали:
- catalog.items() даёт пары (product_id, product) — удобно для фильтрации.
- результатом делает список id, потому что порядок нам здесь не критичен, а хранить просто.
Делаем модель чуть прочнее
Сейчас items — список словарей. Это гибко, но есть риск опечатки в ключе. Мы можем стандартизировать ключи и добавить проверку.
Например, функция добавления позиции в заказ:
def add_item(order, product_id, qty):
if qty <= 0:
raise ValueError("qty must be positive")
order["items"].append({"product_id": product_id, "qty": qty})
Теперь вы не пишете вручную словарь позиции каждый раз, а пользуетесь «строителем». Это повышает надёжность модели, не меняя её сути.
Почему это читаемо и масштабируется
Мы не пытались заранее придумать «идеальную архитектуру».
Мы взяли реальную структуру:
- каталог → товары
- заказ → позиции
- позиция → ссылка на товар + количество
и построили её минимально адекватным способом.
Серьёзность тут в том, что:
- dict даёт семантические имена полям;
- list даёт естественное представление «много элементов»;
- вложенность повторяет то, как вы мысленно представляете задачу.
Если модель совпадает с головой — код читается.
Практика на том же примере
Никаких новых сюжетов. Развиваем эту же модель.
- Добавьте поле status у заказа ("new", "paid", "shipped").
- Напишите функцию, которая возвращает только заказы со статусом "paid".
- Сделайте функцию order_total(order, catalog), которая возвращает сумму заказа, не печатая чек.
- Добавьте скидку на категорию: функция принимает категорию и процент скидки, и возвращает выручку с учётом скидки только на товары этой категории.
Эти задачи заставят вас ещё раз пройти те же цепочки доступа — и закрепят чувство вложенных структур как нормального рабочего инструмента.
05.12.2025
0
33
Комментарии
0