Списковые выражения (list comprehensions) — это лаконичный способ создания списков на основе существующих итерируемых объектов с помощью короткого синтаксиса:
squares = [x**2 for x in range(10)]
Эта запись эквивалентна:
squares = [] for x in range(10): squares.append(x**2)
У list comprehensions есть несколько преимуществ:
evens = [x for x in range(10) if x % 2 == 0];Аналог с map():
def f(x): return x**2 squares = list(map(f, range(10)))
map быстрее на больших данных, если используется функция, уже существующая на C, и подходит для применения одной функции ко всем элементам. List comprehension — для любых выражений, а не только готовых функций. Фор-цикл гибче, но громоздок.
Почему в выражении вида
[x for x in range(10)]переменная x после выполнения списка оказывается доступна вне выражения в Python2, но не в Python3?
Ответ: В Python2 переменная цикла (x) сохраняет своё значение после выполнения list comprehension. В Python3 — она "изолируется" и вне списка недоступна, что предотвращает нежелательные побочные эффекты.
Пример:
# Python 2.x: [x for x in range(3)] print(x) # x == 2 # Python 3.x: [x for x in range(3)] print(x) # NameError: name 'x' is not defined
История 1
Один разработчик на крупном проекте хотел фильтровать и создавать новый список через list comprehension:
my_list = [item.transform() for item in data if item.is_valid()]
Но операция item.transform() вызывала ошибку, если item.is_valid() возвращал False. Однако функция проверки была написана с потенциальным side effect, и в итоге list comprehension неочевидно ломал части кода с побочными эффектами.
История 2
В проекте при миграции с Python2 на Python3 разработчик был уверен, что переменная цикла останется доступной:
[x for x in range(5)] print(x) # Ожидал получить 4, но получил NameError.
Это вызвало баг в циклической логике, где переменная должна была оставаться востребованной вне comprehension.
История 3
Использование вложенных списковых выражений без явного указания уровней:
def flatten(matrix): return [cell for row in matrix for cell in row]
У новичков часто возникают ошибки из-за ошибочного порядка обхода (например, [cell for cell in row for row in matrix] или лишнего вложения), что приводит к неверному результату — одномерный список вместо двумерного или наоборот.