Zanurkuj w Pythonie/Wyrażenia lambda

Wyrażenia lambda

edytuj

Python za pomocą pewnych wyrażeń pozwala nam zdefiniować jednolinijkowe mini-funkcje. Te tzw. funkcje lambda są zapożyczone z Lispa i mogą być użyte wszędzie tam, gdzie potrzebna jest funkcja.

Przykład. Tworzymy funkcje lambda
>>> def f(x):
...     return x*2
...     
>>> f(3)
6
>>> g = lambda x: x*2      #(1)
>>> g(3)
6
>>> (lambda x: x*2)(3)     #(2)
6
  1. W ten sposób tworzymy funkcję lambda, która daje ten sam efekt jak normalna funkcja nad nią. Zwróćmy uwagę na skróconą składnię: nie ma nawiasów wokół listy argumentów, brakuje też słowa kluczowego return (cała funkcja może być tylko jednym wyrażeniem). Funkcja nie posiada również nazwy, ale może być wywołana za pomocą zmiennej, do której zostanie przypisana.
  2. Możemy użyć funkcji lambda bez przypisywania jej do zmiennej. Może taki sposób korzystania z wyrażeń lambda nie jest zbyt przydatny, ale w ten sposób możemy zobaczyć, że za pomocą tego wyrażenia tworzymy funkcję jednolinijkową.

Podsumowując, funkcja lambda jest funkcją, która pobiera dowolną liczbę argumentów (włączając argumenty opcjonalne) i zwraca wartość, którą otrzymujemy po wykonaniu pojedynczego wyrażenia. Funkcje lambda nie mogą zawierać poleceń i nie mogą zawierać więcej niż jednego wyrażenia. Nie próbujmy upchać zbyt dużo w funkcję lambda; zamiast tego jeśli potrzebujemy coś bardziej złożonego, zdefiniujmy normalną funkcję.

Funkcje lambda w prawdziwym świecie

edytuj

Poniżej przedstawiamy funkcje lambda wykorzystaną w apihelper.py:

    processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s)

Zauważmy, że użyta jest tu prosta forma sztuczki and-or, która jest bezpieczna, ponieważ funkcja lambda jest zawsze prawdą w kontekście logicznym. (To nie znaczy, że funkcja lambda nie może zwracać wartości będącej fałszem. Funkcja jest zawsze prawdą w kontekście logicznym, ale jej zwracana wartość może być czymkolwiek.)

Zauważmy również, że używamy funkcji split bez argumentów. Widzieliśmy już ją użytą z jednym lub dwoma argumentami. Jeśli nie podamy argumentów, wówczas domyślnym separatorem tej funkcji są białe znaki (czyli spacja, znak nowej linii, znak tabulacji itp.).

Przykład. split bez argumentów
>>> s = "this   is\na\ttest"  #(1)
>>> print s
this   is
a	test
>>> print s.split()           #(2)
['this', 'is', 'a', 'test']
>>> print " ".join(s.split()) #(3)
'this is a test'
  1. Tutaj mamy wieloliniowy napis, zdefiniowany przy pomocy znaków sterujących zamiast użycia trzykrotnych cudzysłowów. \n jest znakiem nowej linii, a \t znakiem tabulacji.
  2. split bez żadnych argumentów dzieli na białych znakach, a trzy spacje, znak nowej linii i znak tabulacji są białymi znakami.
  3. Możemy unormować białe znaki poprzez podzielenie napisu za pomocą metody split, a potem powtórne złączenie metodą join, używając pojedynczej spacji jako separatora. To właśnie robi funkcja info, aby zwinąć wieloliniowe notki dokumentacyjne w jedną linię.

Co więc właściwie funkcja info robi z tymi funkcjami lambda, dzieleniami i sztuczkami and-or?

    processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s)

processFunc jest teraz referencją do funkcji, ale zależną od zmiennej collapse. Jeśli collapse jest prawdą, processFunc(string) będzie zwijać białe znaki, a w przeciwnym wypadku processFunc(string) zwróci swój argument niezmieniony.

Aby to zrobić w mniej zaawansowanym języku (np. w Visual Basicu), prawdopodobnie stworzylibyśmy funkcję, która pobiera napis oraz argument collapse i używa instrukcji if, aby określić czy zawijać białe znaki czy też nie, a potem zwracałaby odpowiednią wartość. Takie podejście nie byłoby zbyt efektywne, ponieważ funkcja musiałaby obsłużyć każdy możliwy przypadek. Za każdym jej wywołaniem, musiałaby zdecydować czy zawijać białe znaki zanim dałaby nam to, co chcemy. W Pythonie logikę wyboru możemy wyprowadzić poza funkcję i zdefiniować funkcję lambda, która jest dostosowana do tego, aby dać nam dokładnie to (i tylko to), co chcemy. Takie podejście jest bardziej efektywne, bardziej eleganckie i mniej podatne na błędy typu "Ups! Te argumenty miały być w odwrotnej kolejności...".