Сервис сопоставляет записи из двух источников и должен обнаружить рассинхронизацию длин. Как завершится выполнение этого кода?
left = [101, 102, 103]
right = ["ok", "retry"]
pairs = zip(left, right, strict=True)
try:
print(list(pairs))
except ValueError as error:
print(type(error).__name__)
Код напечатает ValueError. Исключение возникнет при попытке получить третий элемент: второй итератор уже исчерпан, тогда как первый содержит дополнительные данные. Обычные пары, полученные до обнаружения рассинхронизации, не будут напечатаны, потому что list() не завершится успешно.
Обычный zip() останавливается, как только заканчивается самый короткий входной итерируемый объект. Такое поведение удобно для обработки общего префикса, но может незаметно скрыть ошибку: часть данных более длинного источника будет проигнорирована.
Параметр strict=True появился в Python 3.10 как средство явно проверять равенство длин входных последовательностей. Он сохраняет ленивую природу zip(), но превращает рассинхронизацию в ValueError вместо молчаливого усечения.
В примере первые две пары корректны: (101, "ok") и (102, "retry"). Однако для третьей пары элемента в right нет, поэтому продолжать сопоставление безопасно нельзя.
Если использовать обычный zip(left, right), результатом станет список из двух пар без какого-либо сигнала об ошибке. Это опасно в ETL-процессах, синхронизации записей, пакетной обработке и проверке соответствия идентификаторов и статусов.
zip() возвращает ленивый итератор. Вызов zip(left, right, strict=True) сам по себе не перебирает входные данные; проверка выполняется по мере вызова next() внутреннего итератора.
При запросе первой и второй пары оба источника успешно выдают элементы. При запросе третьей пары один источник завершается раньше другого, и zip() возбуждает ValueError. Точный текст сообщения не следует использовать как надежный программный контракт: проверять следует тип исключения и сам факт рассинхронизации.
Если источники имеют одинаковую длину, strict=True не меняет результат:
Проверка остается ленивой, поэтому ошибка может возникнуть не при создании объекта zip, а значительно позже — во время обхода. Если потребитель прочитал только первые элементы и остановился, рассинхронизация еще может не обнаружиться.
Пусть один источник возвращает идентификаторы заказов, а другой — рассчитанные суммы. Вариант с обычным zip() прост и ленив, но при потере части сумм молча обработает только общий префикс. Ручная проверка len() заранее подходит для списков, однако не работает напрямую для потоковых итераторов и требует отдельной логики.
Выбранный вариант — zip(..., strict=True) при контракте, требующем одинакового числа элементов. Он сохраняет потоковую обработку и сразу сигнализирует о нарушении соответствия. Компромисс состоит в том, что ошибка обнаруживается только при продвижении итератора до места рассинхронизации, а уже полученные элементы могут успеть вызвать побочные эффекты, если обработка выполняется по одному элементу до полного потребления.
Чем отличается strict=True от проверки результата после list()?
При strict=True несоответствие обнаруживается во время итерации, без необходимости заранее материализовать все входные данные. Проверка после list(zip(...)) уже слишком поздняя для обычного zip(): лишние элементы были молча отброшены, и узнать об этом по результату нельзя.
Возникает ли ошибка при создании объекта zip?
Нет. Создание zip только сохраняет переданные итерируемые объекты и параметры. ValueError возникает при последующем получении элемента, когда реализация обнаруживает, что один источник завершился раньше другого.
Гарантирует ли strict=True, что обработка не имеет частичных эффектов?
Нет. До ошибки потребитель уже может обработать несколько корректных пар. Поэтому при операциях, которые нельзя выполнять частично, сначала применяют отдельную стратегию транзакционности или собирают и валидируют данные целиком, а затем выполняют побочные действия.