Showing posts with label funkcje. Show all posts
Showing posts with label funkcje. Show all posts

Wednesday, January 21, 2009

Python - funkcje też posiadają atrybuty

def funkcja():
    pass

funkcja.imie = 'Jan'
funkcja.wiek = 40
Jak widać zawykła funkcja to dla Pythona nadal obiekt, można mu więc (jak zwykłym obiektom) przypisywać różne atrybuty. Może kiedyś, okaże się to użyteczna - ja na razie nie umiem znaleźć zastosowania dla takiej składni.

Monday, January 19, 2009

Python - jeszcze o gwiazdkach

We wpisie "Funkcje o nieograniczonej liczbie parametrów" pisałem o tym jak napisać funkcję, której można przekazać nieograniczoną liczbę parametrów. Ostatnio zacząłem się zastanawiać jak je przekazać. Weźmy funkcję, która będzie tylko wypisywała przekazane jej parametry

krotka = (1,2,3)
slownik = {'a': 'b', 'c': 'd', 'e': 'f'}

def fun(*args, **kwargs):
    print args
    print kwargs


Pytanie brzmi. Jak używajć krotki zrobić wywołanie funkcjie w stylu fun(1,2,3). Ktoś powie.. "No jak to jak ? fun(krotka)." A właśnie, że nie bo fun krotka to przekazanie jednego parametru do funkcji - będącego krotką i wywłanie będzie wyglądło tak:

fun((1,2,3))

Czyli przekazujemy krotkę parametrów a nie trzy parametry. No a słownik ? Jak przekazać do funkcji słownik tak aby wywołanie wyglądało następująco.

fun(a='b', c='d', e='f')

Zobaczmy :) ja to wygląda

fun(krotka)
((1, 2, 3),)
{}


fun(slownik)
({'a': 'b', 'c': 'd', 'e': 'f'},)
{}


A teraz rozwiązanie :)

fun(*krotka)
(1,2,3)
{}

fun(**slownik)
()
{'a': 'b', 'c': 'd', 'e': 'f'}

fun(*krotka, **slownik)
(1,2,3)
{'a': 'b', 'c': 'd', 'e': 'f'}

Widać składnia "gwiazdek" działa nie tylko przy tworzeniu funkcji ale również gdy chcemy aby krotka lub słownik "rozłożyły się" na parametry funkcji nie zaś zostały przekazane jako jeden parametr :) Do zapamiętania !

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 !

Saturday, January 3, 2009

Python - parametry nazwane i nie nazwane

Weźmy na warsztat funkcję, która przyjmuje kilka parametrów:
def dane(imie="Jan", nazwisko="Kowalski", wiek=45):
print "Imię: " + imie
print "Nazwisko: " + nazwisko
print "Wiek: " + str(wiek)
Funkcja ma trzy parametry. Podajemy jej imię, nazwisko, wiek a jej zadaniem jest ich wyświetlenie w trzech kolejnych linijkach tych danych.

Nie interesuje nas wcale co ta funkcja robi (mogłaby robić kawę i nie miałoby to większego znaczenia), ale o parametry jakie przyjmuje :)
Zajmiemy się w tym newsie dokładnie tym jak my możemy je przekazywać :)

Funkcja posiada wartości domyślne dla wszystkich parametrów. Tak więc wiadomo, że wywołanie funkcji w postaci dane() wyświetli

Imię: Jan
Nazwisko: Kowalski
Wiek: 45


Przekazywanie parametrów, które przychodzi na myśl każdemu kto programował w czymś innym niż python to na przykład opcje (nie będę wypisywał wyjścia, tylko po komentarzu przyjęte wartości).


dane("Kazimierz") # Kazimierz, Kowalski, 45
dane("Zbigniew", "Kroten") # Zbigniew, Kroten, 45
dane("Marta", "Narlicka", 15) # Marta, Narlicka 15


I wszystko pięknie :) Gdy nie podamy jakiegoś parametru - podstawiana jest wartość podana w deklaracji funkcji (domyślna). Więc co jest takiego w Pythonie ? No właśnie czas na mały show.

Python obsługuje tak zwane parametry. Ponieważ nie mam do czego porównać powiem na czym polega. Otóż możesz podać nazwę parametru, który ustawiasz. Na przykład gdybyś chciał ustawić tylko wiek i imię (i nazwisko zostawić domyślne) możesz napisać:


dane(imie="Joanna", wiek=27) # Joanna, Kowalski, 27


Szalenie użyteczne jest to gdy chcemy ustawić tylko kilka konkretnych parametrów naszej funkcji (albo metody) omijając pozostałe. Dzięki temu nasze funkcje są bardziej elastyczne :) i ładniej wyglądają. Nazwy podane przed znakiem równości oznaczają te same nazwy, które podaliśmy w trakcie deklaracji funkcji :)
Gdy chcemy ustawić jego parametr podajemy po prostu: nazwa_parametr=wartosc. Ot cała filozofia :]

Dodam tylko, że w związku z całym zachwytem należy przeczytać o ograniczeniach związany z tym cudem :) nie mniej jest to przyjemna cecha Pythona :] Wstępnie Rubowcy mogą tylko pozazdrościć (na razie funkcjonalność ta jest tylko symulowana w Ruby na hashach).

Friday, December 26, 2008

Python - wywoływanie obiektu

Python umożliwia potraktowanie obiektu jak funkcji, czyli ... wywołania obiektu :) Każdy z was może sam rozważyć taką potrzebę. Widząc jak łatwo obiekt może udawać inne typy (takie jak int, complex czy nawet słownik lub listę), można się zastanowić: skoro obiekt stworzony przez nas tak dobrze może udawać najróżniejsze inne obiekty - dlaczego nie miałby móc udawać funkcji ? To w sumie też obiekt. Tutaj właśnie zaczynają prześwitywać paradygmaty programowania funkcyjnego języka Python :)

A wszystko to w kilku linijkach:
class Klasa(object):

    def __call__(self):
        print "Wywolalem sie ! z wiadomoscia :)"

if __name__ == '__main__':
    obiekt = Klasa()
    obiekt()
Co wynika z powyższego kodu ? Wyposażyliśmy nasz obiekt w metodę __call__. Gdy potem próbujemy wywołać nasz obiekt, potraktować go jak funkcję (fragment obiekt()) python sprawdzi czy istnieje metoda __call__ i jeżeli tak wywoła ją. Metoda ta zachowuje się pod wieloma względami tak jak każa inna funkcja: może przyjmować parametry lub zwracać jakąś wartość :)

Monday, December 22, 2008

Python - funkcje i zmienne z punktu widzenia C++

Będąc pod wrażeniem Thinking in Python chciałem tym wpisem rozpocząć pewne rozważania, które powinny być spostrzeżeniami programisty C/C++ po zderzeniu się z językiem Python.

Języki C/C++ wymagały od nas definiowania funkcji w bardzo restrykcyjny sposób. Musieliśmy podać dokaładnie typ i kolejność przekazywanych parametrów. Zmiana, którejś z tych dwóch powodowało przeładowywanie funkcji. Dodatkowo wymagane było od nas podanie wartości, którą miała zwracać nasza funkcja. Przez co czasami musieliśmy pisać kilka wersji naszej funkcji.

W języku Python wszystko wygląda znacznie prościej. Dlaczego ? Otóż tak jak w języku C/C++ zmienne posiadały konkretny typ - taki a nie inny, w języku Python spotykamy się z pewną szczególną cechą zmiennych. Po pierwsze nie definiujemy explicite ich typów. Są one "rozpoznawane" a może raczej "przyjmowane" przez język programowania na podstawie wartości jakie im przypiszemy.

Co to dla nas oznacza ? Aby w C/C++ stworzyć jakąś uniwersalną funkcję działającą dla różnych typów przekazywanych do niej obiektów musieliśmy używać mechanizmu szablonów. Ponieważ tutaj tworząc funkcję nie podajemy typów parametrów jakie przekazujemy - nasza funkcja działa dla wszystkich danych dla niej przekazanych - o ile operacje, które w niej wykonujemy mają dla tych zmiennych sens.

Dobrym przykładem prezentującym jest na przykład operacja "dodawania" (+).
Dla zmiennej typu string oznacza ona konkatencję (połączenie dwóch napisów).
Dla liczb oznacza to naturalnie ich dodanie.

Rozważmy funkcję dodającą dwie wartości:


def add(a, b):
    return a + b


Nigdzie nie podaliśmy typu zmiennej. Super spróbujmy więc podać wartości różnych typów - zobaczymy co dostaniemy:

add(2,3)

zwróci nam na pewno 5. No dobrze, ale czy zadziała dla liczb zmiennoprzecinkowych ? Oczywiście - jedyny wymaganiem Pythona jest tutaj aby operator, którego używamy (u nas +) był dobrze zdefiniowany dla obiektów, które przekazujemy do funkcji :) z liczbami rzeczywistymi nie ma tutaj problemu.

add(0.1,7.4)

da nam 7.5 :] pięknie. Ba więcej !

add((3 + 2j), (7 + 1.7j))

da nam równie dobry wynik - (10 + 3.7j) :] zgodnie z zasadą - jeżeli dla jakiegoś typu przekazanego do funkcji operacje, które na nim wykonujemy są dobrze określone - wszystko zadziała.
Do tej pory używaliśmy tylko parametrów liczbowych :) Jednak gdyby tak pomyśleć - to operator + jest równie dobrze określony dla napisów - oznacza on po prostu ich konkatenację. Czyli "Merry" + " Christmas" powinno nam wyprodukować "Merry Christmas". Sprawdźmy.

add('Marry', ' Christmas')

Działa :)

Jak widać Python pozawala na tworzenie funkcji, których zachowanie jest bardziej naturalne a wyniki dokładnie takie jakie się spodziewamy :] Specjalne mechanizmy, które służyły nam do osiągnięcia podobnego efektu w C++ zostały jakby "wchłonięte" w naturalne zachowanie się języka programowania.
Zasada iż operatory i działania jakie wykonujemy na obiektach przekazywanych do funkcji tyczy się również tworzonych przez nas typów. Python pozwala na przeładowywanie i definiowanie całego grona operatorów pozwalających nam sterować zachowywaniem się tworzonym przez nas typów danych. Jedną z dostępnych w dokumentacji list takich operatorów znajdziesz pod adresem http://docs.python.org/library/operator.html