Jak czytać zagnieżdżone list comprehensions

Każdy kto programuje w Pythonie z pewnością szybko przekonał się do zalet tzw. list comprehensions. Ten sposób tworzenia list jest nie tylko zgodny z filozofią języka, ale przede wszystkim zgrabny, krótki i czytelny. Czasami jednak zdarza się spotkać konstrukcje, które na pierwszy rzut oka wcale takie czytelne nie są. Przykładem niech będzie fragment, na który natknąłem się podczas jednego z code review:

Czy jego działanie będzie jest oczywiste, a czytelnik w mgnieniu oka potrafi wyobrazić sobie strukturę, która jest tu przetwarzana? Biorąc pod uwagę kiepskie nazewnictwo zmiennych (jeśli mamy do czynienia z kodem, który operuje na konkretnych danych biznesowych, a nie na uogólnionych strukturach, to używanie nazw typu j czy v zupełnie mnie nie przekonuje), pewnie nie za bardzo. Pierwotna lista wyglądała mniej więcej tak:

Założę się, że część osób, które nie miały jeszcze do czynienia z zagnieżdżonymi konstrukcjami tego typu, chcąc napisać list comprehension, spłaszczające ją do postaci listy, zawierającej tylko łańcuchy znakowe DATA#1, DATA#2 itd., zrobi to w następujący sposób:

Jak widać, kod został tu skonstruowany zaczynając od najbardziej zagnieżdżonego fragmentu i podążając ku strukturom zewnętrznym. W myśl tej logiki account_data będzie równe np. ["DATA#1", "DATA#2"],  zaś account_entry będzie całym słownikiem. Tymczasem podejście takie jest błędne, a prawidłowym list comprehension jest w tym wypadku:

Zrozumienie logiki takiej konstrukcji jest banalnie proste, jeśli uświadomimy sobie jak zagnieżdżone list comprehensions mają się do zwykłej pętli for. Dla naszego przypadku musielibyśmy stworzyć taką pętlę:

Teraz wystarczy tylko przerzucić ostatnie entry na początek, pominąć yield, które nie jest nam już potrzebne, a pozostałą część kodu złączyć w jedną linijkę, usuwając przy tym dwukropki. I voila – mamy poprawne potrójne zagnieżdżone list comprehension!

Gdybyśmy chcieli dorzucić gdzieś instrukcję warunkową, to zasady się nie zmieniają i zamiana zagnieżdżonych pętli w list comprehension wciąż pozostaje łatwa i przyjemna, np.:

zamienimy bez problemu na:

Biorąc zaś pod uwagę długość linii, to pewnie bardziej adekwatne będzie przeformatowanie na:

Na sam koniec pozostaje tylko pytanie czy tworzenie tak zanieżdżonych list comprehensions ma w ogóle sens, skoro czytelność tego rozwiązania nie jest wcale oczywista. Czy w Waszych projektach przeszedłby taki kod, czy może poległ już na code review? Jakimi zasadami kierujecie się przy rozwiązywaniu podobnych problemów?

 

.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *