Сравните область видимости переменной цикла и имени, присвоенного оператором присваивания внутри comprehension.
Переменная цикла в comprehension принадлежит его внутренней области видимости и после завершения выражения обычно недоступна снаружи. Имя, которому присвоено значение оператором присваивания внутри comprehension, напротив, связывается во внешней области видимости, где это выражение написано.
Например, внутри функции такое имя станет локальной переменной функции, а на уровне модуля — глобальным именем. Это позволяет сохранить промежуточный результат, но создаёт побочный эффект и риск случайного переиспользования имени.
Comprehension появились как компактный способ строить коллекции по элементам других итерируемых объектов, не раскрывая вспомогательный цикл. Для этого потребовалась отдельная область видимости переменной цикла: иначе comprehension мог бы неожиданно перезаписывать имя, существующее во внешнем коде.
Оператор присваивания в выражении был добавлен позднее, чтобы сохранить промежуточное вычисление внутри выражения и не повторять дорогую операцию. Для него выбрали другое правило: присваивание должно быть заметно во внешней области видимости, иначе сохранённое значение было бы недоступно после завершения comprehension.
Различие становится важным, когда в одном выражении используются и переменная цикла, и промежуточное имя. Если считать их одинаковыми, можно ошибочно ожидать, что оба имени исчезнут после вычисления или, наоборот, оба останутся доступными.
Особенно опасен случай пустого входа: тело comprehension не выполнится, поэтому имя с оператором присваивания может вообще не получить значения. Кроме того, такое присваивание изменяет внешнее состояние, что ухудшает читаемость и может создавать зависимость от предыдущих вычислений.
Comprehension вычисляется в неявном внутреннем контексте. Поэтому переменная цикла не загрязняет внешнюю область видимости. Это правило относится именно к переменной цикла, а не ко всем именам, встречающимся внутри выражения.
Цель оператора присваивания внутри comprehension подчиняется специальному правилу: она связывается в содержащей области видимости. В функции это локальная переменная функции, если не действует особая область вроде nonlocal; на уровне модуля — имя модуля.
Результат — ([0, 1, 4], 4): number не становится переменной вызывающего кода, а saved остаётся локальным именем функции. Оператор присваивания здесь вычисляется на каждой итерации, поэтому после завершения в saved находится последнее присвоенное значение.
Если вход пуст, присваивание не выполняется. Поэтому нельзя надёжно использовать такое имя после comprehension без предварительной инициализации или проверки. В классах область видимости comprehension имеет дополнительные особенности, поэтому полагаться на такое присваивание для создания атрибута класса не следует.
Компромисс состоит в краткости против прозрачности. При простом вычислении оператор присваивания может убрать повторную работу, но при сложной логике обычный цикл обычно яснее и лучше показывает жизненный цикл промежуточного значения.
Сервис получает записи и должен включить в результат только те, для которых один раз вычисляется дорогостоящий разбор. Возможны три подхода: повторить разбор в условии и выражении результата, использовать оператор присваивания внутри comprehension или написать обычный цикл.
Повторный разбор проще визуально, но тратит ресурсы. Оператор присваивания устраняет повторное вычисление и сохраняет компактность, однако оставляет имя во внешней области и может скрыть побочный эффект. Обычный цикл длиннее, зато явно показывает обработку ошибки, пропуск записи и накопление результата.
Для короткого чистого преобразования выбирают comprehension с присваиванием, предварительно контролируя случай пустого входа. Если разбор может завершаться исключением, требует нескольких ветвлений или влияет на метрики, предпочтителен обычный цикл: его поведение проще тестировать и сопровождать.
Оно не получит нового значения. Если имя раньше не существовало, обращение к нему вызовет NameError; если существовало, сохранится его прежнее значение. Поэтому использование такого имени после потенциально пустого comprehension без явной инициализации — источник ошибок.
Нет, цель именованного выражения не должна совпадать с переменной цикла того же comprehension. Это ограничение предотвращает неоднозначность: переменная цикла управляется механизмом итерации, а оператор присваивания должен сохранять отдельное промежуточное имя во внешней области.
Потому что тогда его нельзя было бы использовать после вычисления выражения — например, для проверки последнего вычисленного результата или повторного применения значения без дублирования операции. Отдельная область оставлена для переменной цикла, чтобы не загрязнять внешнее пространство имён, а именованному присваиванию намеренно придано внешнее связывание. Цена этого решения — побочный эффект, который нужно учитывать при проектировании кода.