Поведение переменных в list-comprehension (Python 3)

Поведение переменных в list-comprehension (Python 3)

@python_quiz

Разберем этот квиз

Коротко: приведённый код напечатает

text

Почему — разберём пошагово.

Код из задания

python
  • Внутренняя list-comprehension [i for i in range(3)] генерирует список [0, 1, 2].
  • Внешняя list-comprehension затем итерируется по этому списку и локально называет переменную i, собирая те же значения в res.
  • Важно: в Python 3 переменные, объявленные в list-comprehension, существуют в собственной временной области видимости и не «протекают» (не переопределяют) переменные внешнего окружения.

Поэтому внешняя переменная i, которая была строкой 'outer', остаётся нетронутой, а в res попадает [0, 1, 2].

Демонстрация различий (comprehension vs for-loop)

Чтобы почувствовать разницу, сравним с обычным циклом for, где переменная цикла влияет на внешнюю область видимости:

python

Здесь переменная i цикла for присваивается в той же области видимости, где была 'outer', поэтому она перезаписывается и в конце становится 2.

Историческая ремарка: поведение в Python 2

В Python 2 list-comprehension «протекали» — переменная цикла оставалась в внешней области видимости. Из-за этого тот же код в Python 2 вёл бы себя иначе и переменная i могла бы стать 2. Это одна из причин, почему в Python 3 поведение изменили — чтобы избежать неожиданных побочных эффектов.

(Пример для понимания поведения в старых версиях; запускать на Python 3 приведённый код так, как указывалось выше, не следует.)

Анализ вариантов ответов (кратко)

  • Вариант, соответствующий выводу outer [0, 1, 2]: соответствует поведению Python 3 — переменные comprehension локальны.
  • Вариант 2 [0, 1, 2]: описывает ситуацию, когда переменная цикла «утекла» в внешнюю область видимости (поведение, характерное для Python 2 или для обычного for-цикла).
  • Вариант outer [0, 1, 2, 2]: не соответствует логике выполнения — ни одна из comprehension-операций не производит лишнего элемента 2.
  • NameError: name 'i' is not defined — не наступит, потому что внешняя i была определена до comprehension и не была уничтожена.

Выводы и рекомендации

  • В Python 3 list-comprehension создают собственную область видимости для переменных цикла — это предохраняет внешний код от непреднамеренного перезаписывания переменных.
  • Если вам нужно сохранить значение переменной цикла после итерации (например, последнее значение), используйте явный цикл for.
  • Для читаемости кода лучше давать переменным в comprehensions имена, которые не совпадают с названиями переменных во внешней области видимости, чтобы избежать путаницы.

Небольшой чек-лист:

  • Нужен доступ к переменной цикла после построения списка? — Используйте for.
  • Хочется избежать побочных эффектов? — list-comprehension в Python 3 безопаснее.

Report Page