Upraszczamy kod w Pythonie - map(), filter(), reduce()
Jakiś czas temu na blogu Dawida Kozaka (swoję drogą bardzo polecam i równocześnie pozdrawiam Autora) natrafiłem na ciekawy wpis dotyczący upraszczania kodu Javascript przy wykorzystaniu funkcji .map(), .filter() i .reduce(). Zainspirowało mnie to do napisania niniejszego artykułu, który w zamierzeniu ma przedstawić zastosowanie tych funkcje oraz pokazać alternatywne metody w języka Python.
Funkcje (map, filter) dostępna jest w bibliotece standardowej Pythona zarówno w wersji 2 jak i wersji 3 języka Python. Funkcje reduce w wersji 3 Pythona została przeniesiona do modułu functools. Pomiędzy wersjami 2 i 3 różnica jest w typie zwracanych wartości. Dla wersji 2 jest to lista, a dla wersji 3 iterator.
Zacznijmy od początku czyli funkcji map().
map()
Funkcja map() daje nam możliwość wykonania zadanej funkcji dla każdego elementu kolekcji.
map(function, element_iterowalny)
Rozważmy taki problem: porzebujemy napisać funckję, która podwoi wartości elementów całkowitych tablicy przekazanych jako argument. dla danej tablicy liczb całkowitych potrzebujemy napisać funkcję, która zwróci nam nową tablicę z podwojoną wartością każdego elementu. Napiszmy najpierw funckję która zrobi to z wykorzystaniem pętli. Będzie przetwarzać element po elemencie w kolejnych iteracjach.
W wyniku wykonania programu otrzymamy:
Teraz zrefaktorujmy kod funkcji double() wykorzystując map()
W Pythonie w wersji 2 funkcja map() zwraca listę, w wersji 3 zwraca iterator, dlatego w tym przypadku w funkcji double() konwertujemy go do listy za pomocą list(). Prosciej? Zdecydowanie prościej.
W powyższym przykładzie do podwajania wartości użyliśmy funkcji anonimowej lambda. Nie jesteśmy w tym miejscu ograniczeni wyłącznie do funkcji anonimowych i możemy korzystać z wcześniej napisanych funkcji zewnętrznych. Zobaczmy jak wygląda użycie takiej zewnętrznej funkcji, którą przekażemy do map():
Jak się okazuje w języku Python mamy do dyspozycji wyrażenia listowe (ang. list comprehensions), które w tym przypadku także możemy zastosować (zamiast map()):
Równie prosto, a nawet można by powiedzieć, że czytelniej.
Zdarzają się sytuacje, w których będziemy potrzebowali aby funkcja modyfikjąca otrzymała dodatkowe argumenty. Oto jak powinniśmy napisać kod, aby działał jak należy:
Niektórzy z Was mogą zadać teraz pytanie: no dobrze mamy wyrażenia listowe i funkcję map(), to które z nich należy stosować? które rozwiązanie jest lepsze? Na to pytanie nie ma jednoznacznej odpowiedzi. W przeważającej większości przypadków nie będzie miało znaczenia, którą z tych metod będziemy stosować (poza przypadkiem, że na przykład w konkretnym projekcie czy zespole narzucona jest któraś z tych konwencji). Wyrażenia listowe uważa się za bardziej “pythonowe” (tzw. pythonic way), natomiast map() bliższa jest entuzjastom programowania funkcyjnego. Gdyby ktoś był niezdecydowany, którego z tych rozwiązań używać, to polecam zapoznanie się z bardziej technicznymi aspektami obu rozwiązań.
filter()
Funkcja filter() tworzy nową listę elementów na podstawie wejściowej listy elementów, wybierając tylko te wartości, dla których funkcja testując zwróci prawdę (True). Jak to miało miejsce w przypdaku omawiania map() zacznijmy od przedstawienia problemu i rozwiązania sposobem konwencjonalnym:
API serwisu pogodowego zwraca nam lsitę miast z określonych województw wraz z aktualną temperaturą. Należy utworzyć listę tylko tych miast, w których temperatura jest dodatnia.
Za pomocą funkcji filter() realizujemy zadanie w sposób następujący:
i za pomocą wyrażeń listowych:
Parafrazując słowa Panormixa - “prościej się nie da” ;)
reduce()
Przyszła teraz pora na funkcję reduce(). Funkcja ta jest bardzo użyteczne przy przeprowadzaniu rożnego rodzaju obliczeń na elementach listy (tablicy) i zwracaniu obliczonego wyniku. Stosuje ona obliczenia kroczące (ang. rolling computation) dla kolejnych par wartości elementów listy.
Pokażmy jej działanie na danych z poprzedniego przykładu, tylko zmieńmy sobie cel. Naszym celem niech będzie obliczenie średniej temperatury z temperatur w poszczególnych miastach. Do obliczenia średniej potrzebna jest suma wartości temperatur i liczba miast.
Tradycyjnie na początek metoda konwencjonalna:
Celowo pomijam tutaj sprawdzenie czy ilość elementów data_from_api nie jest przypadkiem równa 0!
Użyjmy teraz reduce():
i wyrażeń listowych
Kończąc, zachęcam Was do eksperymentowania z kodem z różnymi rozwiązaniami związanymi zarówno z funkcjiami map(), filter(), reduce() jak i z wyrażeniami listowymi w Pythonie. Zapraszam też goroąco do komentowania i dzielenia się spostrzeżeniami.
Zostaw komentarz