2.30. Wyrażenia listowe (comprehensions)

Wyrażenie składane (comprehension) buduje nową listę, zbiór, słownik lub generator z istniejącego obiektu iterowalnego w pojedynczym wyrażeniu. Zastępuje powszechny wzorzec polegający na rozpoczęciu od pustego kontenera i dołączaniu elementów w pętli.

2.30.1. Wyrażenia listowe

squares = [x * x for x in range(5)]
print(squares)

Wynik:

[0, 1, 4, 9, 16]

Ta sama pętla zapisana w pełnej formie:

squares = []
for x in range(5):
    squares.append(x * x)

Forma wyrażenia składanego to jedno wyrażenie, które buduje listę w miejscu. Nie ma squares = [] ani .append – wynikiem jest wartość wyrażenia składanego.

Wyrażenie "[f(x) for x in xs if cond]" z opisem: wiodące wyrażenie jest wynikiem, klauzula for nazywa zmienną pętli, a klauzula if filtruje, które elementy są zachowywane.

Wiodące wyrażenie wytwarza każdy element; klauzula for nazywa zmienną pętli; opcjonalna klauzula if odfiltrowuje elementy.

2.30.2. Filtrowanie za pomocą if

Opcjonalna klauzula if zachowuje tylko pasujące elementy:

evens = [x for x in range(10) if x % 2 == 0]
print(evens)

Wynik:

[0, 2, 4, 6, 8]

Filtr działa przed wiodącym wyrażeniem – najpierw sprawdzane jest x % 2 == 0; tylko pasujące wartości trafiają do x dla wyniku.

2.30.3. Wyrażenia słownikowe i zbiorów

Ten sam kształt działa z literałami słownika i zbioru.

Wyrażenie słownikowe ma parę key: value przed klauzulą for:

squares = {x: x * x for x in range(5)}
print(squares)

Wynik:

{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

Wyrażenie zbioru używa nawiasów klamrowych i pojedynczego wyrażenia:

unique_lengths = {len(w) for w in ["a", "bb", "c", "bb"]}
print(unique_lengths)

Wynik:

{1, 2}

2.30.4. Wyrażenia generatorowe

Nawiasy okrągłe wytwarzają wyrażenie generatorowe zamiast listy. Wartości są obliczane pojedynczo, na żądanie:

total = sum(x * x for x in range(1000))

Lista o milionie elementów nigdy nie jest budowana. Wartości przepływają jedna po drugiej do sum(), która je dodaje i odrzuca każdą w miarę postępu.

Wyrażenia generatorowe są właściwym wyborem przy podawaniu wartości do funkcji redukującej (sum(), max(), any(), all()) lub do dowolnego innego kodu konsumującego iterator – oszczędzają pamięć, którą zajęłaby równoważna lista.

2.30.5. Kiedy nie używać wyrażenia składanego

Wyrażenia składane są zwięzłe, ale nie zawsze bardziej czytelne. Sięgnij po zwykłą pętlę for, gdy:

  • Ciało wymaga więcej niż jednej instrukcji (wyrażenie składane mieści dokładnie jedno wyrażenie).

  • Ciało ma efekty uboczne (drukowanie, zapis do pliku) – wyrażenia składane służą do budowania kolekcji, a nie do wykonywania akcji.

  • Filtr lub przekształcenie ma tak wiele części, że wyrażenie składane przestaje się czytać od lewej do prawej.