Monday, January 5, 2009

Python - funkcje o nieograniczonej liczbie parametrów

Tak chyba należałoby to zatytułować :) Cóż to ach cóż :]
Wyobraźmy sobie funkcję, która miałaby sumować wszystkie podane jej w parametrze liczby. Nie chcemy ograniczać się w ich ilości. Funkcja ma dodawać liczby do siebie i zwracać ich sumę. Czy podamy ich 3 czy 100 - ma działać :)

Pierwsze co przychodzi nam do głowy to może po prostu podawać krotkę parametrów. W sumie - czemu nie. W porządku, jednak to załóżmy iż nasz zmysł programistycznej estetyki cierpi na tym i nie daje nam spokoju. Czy funkcja w Pythonie może przyjmować nieskończenie parametrów.

Może :) "To super !" - ktoś powie. Super :) ale nie, jeżeli trzeba to tłumaczyć, bo w Pythonie całość można zrealizować na dwa sposoby. Ale pokolei.

Pierwsza metoda polega na podaniu w którejś kolejności jako parametr funkcji nazwy zaczynającej się od jednej gwiazdki (w naszym przykładzie będzie to jedyny parametr choć nie musi tak być). Na razie obejrzemy sobie tylko jak wygląda.
def suma(*args):
    print args

suma('ja', 3.4, (9 + 1j)) # ('ja', 3.4, (9 + 1j))
suma(1,2,3) # (1, 2, 3)

Co widzimy ? Otóż podczas gdy w liście parametrów funkcji nazwa jednego z argumentów jest poprzedzona gwiazdką (parametr ten nie musi nosić nazwy args jednak tak się w Pythonie przyjęło) możemy zajrzeć do niego uzyskując dostęp do wszystkich przekazanych do funkcji wartości. Okazuje się (jak widać), że args jest od "wnętrza" krotką, która przetrzymuje kolejno wszystkie przekazane wartości. Skoro już to wiemy :) stworzenie funkcji sumującej wszystkie przekazane do niej liczby jest banalne.
def suma(*args):
    wynik = 0
    for element in args:
        wynik += element
return wynik
Ponieważ args zawiera w sobie (krotka) wszystkie przekazane do funkcji parametry możemy przebiec się po nich pętlą for a następnie pododawać je do siebie :) i zwrócić wynik. Dzięki użyci w definicji *args możemy użyć dowolnej ilości elementów. Wszystkie znajdą się w naszej krotce i będziemy mogli je posumować :] Sprawdźmy działanie naszej funkcji:
suma(1,2,3)
suma(1,2,3,4,5,6,7,8,9,20)

nieważne ile parametrów podamy :) zadziała :]

No dobrze to jeden sposób :) Działa dobrze dla parametrów nienazwanych. Czasami na przykład chcielibyśmy mieć funkcję obsługującą "najróżniejsze parametry" i móc sprawdzić jakie nazwy im nadano w trakcie przekazywania ich. Gdy spróbujemy czegoś takiego suma(a=1,b=2,c=3) w naszej obecnej funkcji dostaniemy błąd. Wszystko dlatego, że za obsługę parametrów nazwanych odpowidzialna jest zmienna (argument) o nazwie poprzedzonej dwoma gwiazdkami ** zwyczajowo nazywana **kwargs.

Stwórzmy sobie funkcję test, która pokaże nam jak wygląda przekazywanie parametrów:
def test(*args, **kwargs):
    print args
    print kwargs
i uruchommy ją dla zobaczenia jak działa **kwargs test(1,2,3,d=4,e=5,f=6)
(1, 2, 3)
{'e': 5, 'd': 4, 'f': 6}


Jak widzimy :] Wszystkie parametry przekazane jako parametry nazwane występują pod zmienną kwargs (zmienna nazywana tak zwyczajowo jednak chodzi o tą poprzedzoną dwoma gwiazdkami). Jest to z oczywistych przyczyn (musi zawierać nazwę i wartość jej przypisaną) słownik :) Przy czym zawiera on wyłącznie parametry przekazane jako nazwane. Nadal wszystkie parametry przekazane jako nienazwane znajdziemy w *args.

To w sumie tyle :) Może powiem tylko o pewnej podstawowej rzeczy, o której należy pamiętać podczas wywoływania funkcji gdzie używamy parametrów nazwanych i nienazwanych. Wszystkie parametry nazwane muszą występować na końcu. Innymi słowami gdy wpiszesz już jako parametr nazwany wszystkie kolejne muszą być nazwanymi albo musi być to koniec parametrów :) Sprawdź sam !

No comments: