← назад к разделу

Всё, что было раньше, — инструменты. Теперь три задачи, ради которых их и брали. Каждая встречается в работе еженедельно.

в базе ord-01ord-02ord-03 в выдаче API ord-02ord-03ord-04 пропало: ord-01 лишнее: ord-04 было - стало и стало - было множества отвечают на это одной строкой, а в сообщение кладут сами номера, а не их количество

Разница двух наборов ищется вычитанием множеств: слева — чего не хватает, справа — что появилось лишнего.

Посчитать то, чего нет на экране

Интерфейс показывает список заказов, а вопрос звучит иначе: «на какую сумму заказов в статусе PAID за день?» Такой цифры на экране нет, а в данных — есть.

живой пример

заказы = [
    {"id": "ord-042", "status": "PAID", "total": 4990},
    {"id": "ord-043", "status": "CANCELLED", "total": 890},
    {"id": "ord-044", "status": "PAID", "total": 1780},
]

оплаченные = [з for з in заказы if з["status"] == "PAID"]
сумма = sum(з["total"] for з in оплаченные)

print(f"Оплаченных: {len(оплаченные)}, на сумму {сумма}")
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Неделя бесплатно →

sum() складывает числа из перечня. Рядом живут min(), max() и len() — этой четвёрки хватает для большинства сводок.

Считать по группам удобно словарём-накопителем:

живой пример

заказы = [
    {"id": "ord-042", "status": "PAID", "total": 4990},
    {"id": "ord-043", "status": "CANCELLED", "total": 890},
    {"id": "ord-044", "status": "PAID", "total": 1780},
]

по_статусам = {}
for заказ in заказы:
    статус = заказ["status"]
    по_статусам[статус] = по_статусам.get(статус, 0) + 1

print(по_статусам)     # {'PAID': 2, 'CANCELLED': 1}
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Неделя бесплатно →

get(статус, 0) здесь и держит всю конструкцию: для нового статуса счётчика ещё нет, и вместо ошибки берётся ноль.

Сверить итог с составом

Классическая проверка: сумма позиций должна совпадать с итогом заказа. Дефекты такого рода в интерфейсе не видны — их находят только счётом.

живой пример

# ошибка: пример падает намеренно — у второго заказа итог не сходится
заказы = [
    {"id": "ord-042", "items": [{"qty": 1, "price": 4990}], "total": 4990},
    {"id": "ord-043", "items": [{"qty": 2, "price": 890}], "total": 1000},
]

def сумма_сходится(заказ):
    посчитано = sum(п["qty"] * п["price"] for п in заказ["items"])
    return посчитано == заказ["total"]

расхождения = [з["id"] for з in заказы if not сумма_сходится(з)]
assert not расхождения, f"итог не сходится у заказов: {расхождения}"
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Неделя бесплатно →

Обратите внимание на последнюю строку: в сообщение попадают номера проблемных заказов. «Не сходится у трёх» бесполезно, «не сходится у ord-042, ord-107» позволяет сразу открыть их и посмотреть.

С дробями осторожно: 0.1 + 0.2 в любом языке не равно ровно 0.3. Деньги поэтому считают либо в копейках целыми числами, либо сравнивают с допуском:

живой пример

заказ = {"items": [{"qty": 1, "price": 99.90}], "total": 99.9}
посчитано = sum(п["qty"] * п["price"] for п in заказ["items"])

assert abs(посчитано - заказ["total"]) < 0.01
print("сошлось с точностью до копейки")
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Неделя бесплатно →

Найти, что пропало и что лишнее

Две выгрузки — из интерфейса и из базы, из старой версии и из новой. Вопрос всегда один: чего не хватает и что появилось лишнего. Списками это неудобно, а множествами — одна строка.

Множество — набор без порядка и без повторов:

живой пример

было = {"ord-01", "ord-02", "ord-03"}
стало = {"ord-02", "ord-03", "ord-04"}

print(было - стало)      # {'ord-01'}  — пропало
print(стало - было)      # {'ord-04'}  — появилось
print(было & стало)      # {'ord-02', 'ord-03'} — есть в обоих
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Неделя бесплатно →

Из списка словарей множество собирают по ключевому полю:

живой пример

выгрузка_базы = [{"id": "ord-042"}, {"id": "ord-043"}]
ответ = {"items": [{"id": "ord-042"}, {"id": "ord-043"}]}

из_базы = {з["id"] for з in выгрузка_базы}
из_api = {з["id"] for з in ответ["items"]}

пропали = из_базы - из_api
лишние = из_api - из_базы

assert not пропали, f"нет в выдаче API: {sorted(пропали)}"
assert not лишние, f"лишние в выдаче API: {sorted(лишние)}"
print("состав совпал")
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Неделя бесплатно →

sorted() в сообщении не украшение: у множества нет порядка, и без сортировки одно и то же расхождение печатается каждый раз по-разному.

Сравнить не только состав, но и содержимое

Совпадения номеров мало: заказ может быть на месте, но с другой суммой. Тогда сверяют поля:

живой пример

выгрузка_базы = [{"id": "ord-042", "total": 4990}]
ответ = {"items": [{"id": "ord-042", "total": 4990}]}

база = {з["id"]: з for з in выгрузка_базы}     # словарь по номеру

for заказ in ответ["items"]:
    эталон = база[заказ["id"]]
    assert заказ["total"] == эталон["total"], (
        f"{заказ['id']}: в API {заказ['total']}, в базе {эталон['total']}")

print("суммы совпали")
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Неделя бесплатно →

Первая строка — приём, который стоит запомнить: список превращается в словарь по ключу, и дальше любой элемент достаётся мгновенно, без перебора.

Что решать

Банк «Проверки данных»:

Коротко

  • sum(), len(), min(), max() закрывают большинство сводок; счёт по группам — словарь-накопитель с get(ключ, 0).
  • Сверка итога с составом ловит дефекты, невидимые в интерфейсе; в сообщение кладите номера проблемных записей, а не их количество.
  • Деньги в дробных числах сравнивают с допуском либо считают в копейках.
  • Разницу двух наборов ищут множествами: было - стало и стало - было.
  • Список превращают в словарь по ключу, когда нужно быстро находить пару.

Что почитать дальше