Wyrażenia listowe (list comprehensions) to zwięzły sposób tworzenia list na podstawie istniejących obiektów iterowalnych za pomocą krótkiej składni:
squares = [x**2 for x in range(10)]
Ta zapis jest równoważny:
squares = [] for x in range(10): squares.append(x**2)
Wyrażenia listowe mają kilka zalet:
evens = [x for x in range(10) if x % 2 == 0];Odpowiednik z map():
def f(x): return x**2 squares = list(map(f, range(10)))
map jest szybszy na dużych danych, jeśli używana jest funkcja już istniejąca w C, i nadaje się do zastosowania jednej funkcji na wszystkich elementach. List comprehension — do wszelkich wyrażeń, a nie tylko gotowych funkcji. Pętla for jest bardziej elastyczna, ale bardziej rozbudowana.
Dlaczego w wyrażeniu typu
[x for x in range(10)]zmienna x po wykonaniu listy okazuje się dostępna poza wyrażeniem w Pythonie 2, ale nie w Pythonie 3?
Odpowiedź: W Pythonie 2 zmienna pętli (x) zachowuje swoją wartość po wykonaniu list comprehension. W Pythonie 3 — jest "izolowana" i poza listą niedostępna, co zapobiega niepożądanym efektom ubocznym.
Przykład:
# 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
Historia 1
Jeden programista w dużym projekcie chciał filtrować i tworzyć nową listę za pomocą list comprehension:
my_list = [item.transform() for item in data if item.is_valid()]
Jednak operacja item.transform() wywoływała błąd, jeśli item.is_valid() zwróciło False. Funkcja sprawdzająca była napisana z potencjalnym efektem ubocznym, a w efekcie list comprehension nieoczywiście psuł części kodu z efektami ubocznymi.
Historia 2
W projekcie podczas migracji z Pythona 2 do Pythona 3 programista był pewny, że zmienna pętli pozostanie dostępna:
[x for x in range(5)] print(x) # Oczekiwał, że dostanie 4, ale otrzymał NameError.
To spowodowało błąd w logice cyklicznej, gdzie zmienna miała pozostawać będąca użyteczna poza comprehension.
Historia 3
Użycie zagnieżdżonych wyrażeń listowych bez wyraźnego wskazania poziomów:
def flatten(matrix): return [cell for row in matrix for cell in row]
Nowicjusze często napotykają błędy z powodu błędnej kolejności przeszukiwania (np. [cell for cell in row for row in matrix] lub zbędnego zagnieżdżenia), co prowadzi do błędnego wyniku — listy jednowymiarowej zamiast dwuwymiarowej i odwrotnie.