Powiedzmy sobie o kolejnej koncepcji, spotykanej nie tylko w języku Python. Mianowicie: funkcja jako obiekt pierwszej klasy. Co to właściwie znaczy? Jak już się zdążyliśmy przekonać, funkcje są obiektami. Dzięki temu możemy je potraktować jako wartość, podobnie jak liczby, ciągi znaków czy listy. Co z tego wynika? Przede wszystkim to, że funkcja sama w sobie może być argumentem przekazywanym do innej funkcji.
Jednak traktowanie funkcji jako obiektu nie ogranicza się tylko do podawania jej jako argumentu. Wartość można także zwrócić lub przypisać do zmiennej. Python oferuje pełną elastyczność, pozwalając na traktowanie funkcji na równi z innymi wartościami.
Co to znaczy „obiekt pierwszej klasy” w programowaniu?
Kiedy mówimy o „obiektach pierwszej klasy” w programowaniu, mamy na myśli takie elementy, które mogą być przypisane do zmiennych, przekazywane jako argumenty do funkcji oraz zwracane przez funkcje. Ta cecha dodaje ogromną elastyczność do języka, umożliwiając konstruowanie bardziej dynamicznych, elastycznych struktur kodu. Możemy dzięki temu tworzyć na przykład funkcje wyższego rzędu, czyli takie, które przyjmują inne funkcje jako argumenty lub zwracają funkcje jako wynik.
Funkcja w przykładach
Przekonajmy się, co to rzeczywiście znaczy, że funkcja jest obiektem, mogącym być traktowanym jako wartość. Przez to można ją przekazywać jako argument, a także zwracać – jak zostało wspomniane we wstępie.
Stworzymy sobie kilka prostych, wręcz trywialnych funkcji, które pomogą nam zilustrować koncepcję.
Powyżej tworzymy dwie funkcje: jedną dzielącą dwie liczby przez siebie, a drugą mnożącą takie liczby. Obie funkcje mają dwa parametry, by przyjąć argumenty a
i b
. Dodatkowo mamy trzecią funkcję apply
, która przyjmuje parametr fn
, pozwalający na przekazanie funkcji jako argumentu. W ciele tej ostatniej funkcji zwracany jest wynik fn(a, b)
. Zatem jeśli pod fn
będzie funkcja multiply
, a pod a
i b
liczby 10 i 2, nasza funkcja apply
wywoła multiply
na liczbach, które otrzymała.
W przykładzie nazwę funkcji podajemy po prostu jako argument, nie traktując tego inaczej niż przekazania zwykłej wartości. Gdyby to była liczba, czy łańcuch znaków, postąpilibyśmy podobnie. Dzięki temu rozwiązanie jest uniwersalne – nawet nazwę funkcji możemy potraktować jako obiekt, którym przecież jest. Nie musimy postrzegać nazwy funkcji jako czegoś odrębnego od innych obiektów.
Możemy sobie udowodnić, pokazując jak zachowa się nasz kod, gdy zastosujemy ostatnią funkcję do wywołania jednej z dwóch pierwszych.
Wynik nie zaskakuje: jest to po prostu zastosowanie funkcji multiply
na liczbach 10 i 2. Przykład ten jest trochę wymuszony, bo moglibyśmy stosować funkcję bezpośrednio, ale jeśli nastąpiłaby konieczność dynamicznego stosowania funkcji – tutaj zaprezentowano taką możliwość.
Zadania do samodzielnej pracy
Aby lepiej zrozumieć koncepcję funkcji jako obiektów pierwszej klasy, spróbuj samodzielnie wykonać poniższe zadania:
Dynamiczna modyfikacja działania funkcji: Napisz funkcję power_up
, która przyjmuje jako argument funkcję zwracającą liczbę, i zwraca nową funkcję, która podnosi wynik pierwotnej funkcji do kwadratu. Przykładowo: power_up(lambda: 3)
powinno zwrócić 9
.
Funkcja odwrotna: Napisz funkcję inverse
, która przyjmuje jako argument funkcję z jednym parametrem i zwraca nową funkcję, która działa odwrotnie, np. inverse(abs)
powinna zwracać funkcję negate
, która zmienia znak liczby.
Filtrowanie za pomocą funkcji: Stwórz funkcję filter_list
, która przyjmuje listę i funkcję jako argumenty. filter_list
powinna zwrócić tylko te elementy listy, które spełniają warunek podany w funkcji przekazanej jako argument.