В списковом включении переменная цикла совпадает с именем внешней переменной: какое значение останется у внешнего имени после завершения включения?
В Python 3 внешнее имя сохранит прежнее значение. Переменная цикла спискового включения находится в отдельной неявной области видимости и не «протекает» в окружающую область.
Включение создаёт отдельную область для своей переменной цикла, поэтому присваивание имени value внутри него не переназначает внешнее имя.
В Python 2 переменная цикла спискового включения оставалась доступной во внешней области видимости. Это могло неожиданно перезаписывать существующее имя после вычисления списка.
В Python 3 для списковых, словарных и множественных включений используется отдельная неявная область видимости. Такой подход устраняет побочный эффект и согласует включения с общей моделью локальных областей функций.
Совпадение имён во включении может выглядеть безобидно, особенно когда внешняя переменная используется до и после формирования коллекции. Ошибочное предположение о протекании переменной приводит к неверным выводам о состоянии программы.
Важно различать переназначение имени и изменение объекта. Отдельная область защищает внешнее имя от нового связывания, но не делает объекты неизменяемыми и не запрещает изменять общий изменяемый объект.
При выполнении включения Python создаёт отдельную область видимости для переменной цикла. Внешнее имя читается из окружающей области только в тех случаях, когда оно не получает новое значение внутри области включения.
После завершения включения локальное имя переменной цикла уничтожается вместе с этой областью. Поэтому обращение к внешнему имени возвращает его исходное связывание.
Это правило относится к списковым, словарным и множественным включениям. Генераторные выражения также имеют собственную область, но их вычисление откладывается: переменная используется по мере итерации генератора.
Отдельная область не является полной копией внешних данных. Если включение обращается к изменяемому объекту из внешней области и изменяет этот объект, изменение будет видно снаружи. Защищается именно связывание имени, а не состояние объекта.
В обработчике данных внешняя переменная item хранит текущую запись, а разработчик использует то же имя в списковом включении для фильтрации. В Python 3 внешняя запись не заменяется, что делает такой код корректным по семантике, хотя одинаковые имена ухудшают читаемость.
Вариант с намеренно другим именем переменной цикла проще для ревью и снижает риск ошибок при переносе фрагмента в обычный цикл. Его минус — необходимость подобрать дополнительное осмысленное имя, но это предпочтительный компромисс.
Обычный цикл с тем же именем в теле функции ведёт себя иначе: присваивание переменной цикла происходит в текущей области видимости и после завершения цикла имя остаётся доступным. Поэтому нельзя механически переносить вывод о списковом включении на обычный цикл.
1. Относится ли отдельная область видимости к обычному циклу?
Нет. Отдельная область характерна для включений и генераторных выражений. Переменная обычного цикла получает связывание в текущей области, поэтому после цикла обычно остаётся доступной.
2. Что произойдёт, если включение изменяет внешний изменяемый объект?
Изменение может сохраниться, поскольку внешний объект доступен через ссылку. Например, добавление элементов во внешний список изменяет сам список; отдельная область видимости не создаёт его копию. Она лишь не позволяет локальному связыванию имени заменить внешнее.
3. Есть ли разница между списковым включением и генераторным выражением в моменте вычисления?
Да. Списковое включение сразу вычисляет элементы и создаёт список. Генераторное выражение создаёт итератор, а тело выражения выполняется при последующей итерации; это влияет на время обращения к внешним объектам и на момент возможных ошибок или побочных эффектов.