Jedna linia, jedna myśl
Ten wzorzec na pewno już znasz: zaczynasz od pustej listy, przechodzisz pętlą po czymś, czasem filtrujesz i dopisujesz do wyniku.
List comprehension mówi to samo w jednej linii:
Czytaj od lewej do prawej: "nowa lista złożona z n * 2 dla każdego n w numbers". Struktura to [expression for item in iterable].
To nie jest sztuczka tylko z Pythona. Nazywa się to comprehension, bo deklaratywnie opisujesz, co trafia do nowej listy, zamiast rozpisywać mechanikę jej budowania.
Dodawanie filtra
Dopisz klauzulę if po części z pętlą, żeby filtrować:
Drugą linię czytaj tak: "n * n dla każdego n w numbers, ale tylko jeśli n jest nieparzyste".
Równoważna pętla:
Obie wersje są w porządku. Comprehension jest krótsza, a gdy przeczytasz kilka, łatwiej się ją przegląda, bo cały zamiar widać w jednej linii.
Mapowanie i filtrowanie naraz
Możesz przepuszczać wartości przez funkcję i jednocześnie filtrować:
Dla każdego słowa dłuższego niż 3 litery dodaj jego wersję pisaną wielkimi literami.
Zagnieżdżone pętle w comprehension
Dwa for dają pary w stylu iloczynu kartezjańskiego:
Kolejność jest taka sama jak przy zagnieżdżonych pętlach: pierwszy for jest zewnętrzny, drugi wewnętrzny. Czyta się to od lewej do prawej, tak jak odpowiadającą temu pętlę z wcięciami.
Dwa poziomy to mniej więcej granica, za którą zwykła pętla czyta się lepiej. Jeśli dochodzisz do trzech, wróć do pętli.
Dict comprehension i set comprehension
Ten sam pomysł, inne nawiasy:
Na pierwszy rzut oka set comprehension wygląda identycznie jak dict comprehension. Różnica to key: value zamiast pojedynczego wyrażenia. Klamry z : to słownik; klamry bez : to zbiór.
Wyrażenia generatorowe
To niemal bliźniak list comprehension, tylko z nawiasami okrągłymi zamiast kwadratowych i, co najważniejsze, nie buduje listy:
Zauważ, że generator trafił bezpośrednio do sum() i any(), bez dodatkowych nawiasów. Wyrażenia generatorowe to właściwe narzędzie, gdy wywołujący musi przejść po wynikach tylko raz. Przy dużych kolekcjach oszczędzają pamięć w porównaniu z pełną listą.
Kiedy lepiej użyć zwykłej pętli
Comprehensions kuszą. Technicznie da się w nie wcisnąć mnóstwo rzeczy. Nie warto.
Sięgnij po zwykłą pętlę, gdy:
- Przekształcenie ma więcej niż jeden krok. Jeśli potrzebujesz zmiennych pośrednich, rozpisz to.
- Jest w nim złożona obsługa błędów albo rozgałęzienia.
- Każdy, kto czyta tę linię, musi zatrzymać się więcej niż raz, żeby ją zrozumieć.
Zasada, którą stosuję: jeśli potrafię przeczytać comprehension jednym tchem i zrozumieć, co robi, zostaje. Jeśli się potykam, przepisuję ją na pętlę.
Niektóre comprehensions wyglądają sprytnie, ale szkodzą:
Ten sam wynik. Wersja z pętlą ma pięć linii zamiast jednej, ale "dłuższe" nie znaczy "gorsze".
Kilka wzorców, które będziesz powtarzać
Te wzorce pokrywają zaskakująco dużo codziennej pracy z danymi.
Co dalej
Znasz już główne typy kolekcji i comprehension, które je łączy. Następny rozdział: funkcje, czyli zamykanie zachowania w nazwanych jednostkach wielokrotnego użytku.
Najczęściej zadawane pytania
Czym jest list comprehension w Pythonie?
To zwięzła składnia do budowania nowej listy z istniejącego obiektu iterowalnego. [x * 2 for x in numbers] tworzy nową listę, w której każda liczba jest podwojona. Możesz też filtrować: [x for x in numbers if x > 0].
Kiedy nie używać list comprehension?
Wtedy, gdy szkodzi czytelności. Jeśli wyrażenie w środku jest skomplikowane albo mocno zagnieżdżone, zwykła pętla for z dobrymi nazwami zmiennych jest jaśniejsza. Comprehensions są do prostych przekształceń: mapowania i filtrowania. Wszystko bardziej złożone lepiej wygląda w pełnej pętli.
Czym różni się list comprehension od wyrażenia generatorowego?
List comprehension buduje całą listę w pamięci. Wyrażenie generatorowe (ta sama składnia, ale w nawiasach okrągłych) zwraca po jednym elemencie naraz. Używaj generatora, gdy wyniki trafiają do czegoś, co przechodzi po nich raz, na przykład do sum(...), żeby nie tworzyć listy, którą zaraz wyrzucisz.