Всё, что было раньше, — инструменты. Теперь три задачи, ради которых их и брали. Каждая встречается в работе еженедельно.
Разница двух наборов ищется вычитанием множеств: слева — чего не хватает, справа — что появилось лишнего.
Посчитать то, чего нет на экране
Интерфейс показывает список заказов, а вопрос звучит иначе: «на какую сумму заказов в статусе 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).- Сверка итога с составом ловит дефекты, невидимые в интерфейсе; в сообщение кладите номера проблемных записей, а не их количество.
- Деньги в дробных числах сравнивают с допуском либо считают в копейках.
- Разницу двух наборов ищут множествами:
было - сталоистало - было. - Список превращают в словарь по ключу, когда нужно быстро находить пару.
Что почитать дальше
- pytest: первый настоящий тест — как это оформляют в проекте.
- SQL для тестировщиков — вторая половина той же работы: данные берут из базы.