Wednesday, January 14, 2009

Python - classmethods czyli staticmethod na sterydach

Dziś natknąłem się na fajny przykład zastosowania pythonowego classmethod :) Najlepszy jaki widziałem w sieci. Wpis ten więc oprę o ten właśnie przykład.

Po pierwsze jakie cechy posiadają tak zdefiniowane metody:
* można wywołać je zarówno na klasie jak i na obiekcie
* zamiast parametru self (obiektu) przyjmuje parametr cls - klasę dla której została wywołana

Cechy dość ciekawe. Spójrzmy co z nich wynika. Po pierwsze - aby użyć metody zdefiniowanej jako classmethod nie musimy tworzyć obiektu :) możemy wywołać ją na klasie. Jednak ! względem metody statycznej, możemy dowiedzieć się na jakiej klasie ją wywołujemy ! Jest to szalona wygoda :) Na początku napiszmy klasę z metodą statyczną, która nadaje wartość jakiemuś statycznemu parametrowi.
class A(object):
    counter = 0

    @staticmethod
    def setCounter(value):
        A.counter = value

if __name__ == '__main__':
    A.setCounter(10)
Proste. Utworzyliśmy klasę z atrybutem counter oraz prostą metodę statyczną, która pozwala na ustawienie mu wartości. Zauważ pewną ciekawą rzecz. Aby odwołać się do zmiennej counter klasy A, po wejściu do metody statycznej musisz wpisać nazwę klasy "z ręki". Od wewnątrz metoda statyczna jest pozbawiona jakichkolwiek informacji o obiekcie, do którego przynależy i nie ma się jak go chwycić. Nie posiada ani parametru self aby odwołać się odbiektu - a przez niego do klasy, ani nic innego. Jedynym wyjściem aby odwołać się do klasy jest użycie jej nazwy explicite (wprost). Szalenie nieeleganckie rozwiązanie a przede wszystkim niewygodne. Dlaczego ? Gdy klasa zmieni swoją nazwę będzie trzeba zmieniać ją we wszystkich takich wystąpieniach. Ale to nie początek "problemów" zobacz co się stanie gdy odziedziczysz po klasie A.
class B(A):
    pass

if __name__ == '__main__':
    B.setCounter(20)
Gdy teraz wypiszesz A.counter okaże się, że wynosi on 20. Sytuacja jest jasna - klasa B odziedziczyła metodę jednak wciąż wywoływane jest A.counter = value i ustawiana jest wartość dla A. Może być to działanie zamierzone, jednak my chcielibyśmy aby klasy, które dziedziczą po naszej - posiadały swoje własny liczniki. Wystarczy tylko odrobinkę zmienić klasę A i po kłopocie. Retusz będzie dotyczył naszej metody, która powinna wyglądać tak:
@classmethod
def setCounter(cls, value):
cls.counter = value

I to wszystko ? Tak to wszystko :) Przyjrzyjmy się zmianom. Po pierwsze teraz nasza metoda nie jest statyczna - lecz jest metodą oznaczoną jako classmethod. Taką metodę możemy nadal wywoływać na klasie lub na obiekcie. Metody klasy jako pierwszy parametr przyjmują klasę dla której są wywoływane. Tutaj użyliśmy zmiennej o nazwie cls. Dzięki temu nasza metoda stała się uniwersalna. wywołanie metody setCounter dla klasy A będzie wyglądało (poglądowo) tak:
setCounter(A, 10):
    A.counter = 10
a dla B tak:
setCounter(B, 20):
    B.counter = 20
O taki dokładnie efekt nam chodziło :) brawo ! Czas na podsumowanie.

Metody klasy możemy traktować jak pewnego rodzaju dodatek do metod statycznych, który zmienia ich zachowanie. Po pierwsze metody klasy posiadają więcej informacji. Wiedzą dla której klasy są wywoływane co pozwala nam wykorzystać tą cenną informację :) Po drugie nie zmuszają nas do wpisywania explicite nazwy klasy (bo mamy ją w zmiennej) co oszczędza mnóstwo czasu na refaktoryzacji kodu.

http://docs.python.org/library/functions.html#classmethod

Python - sloty

Najprostrzą metodą zdefiniowania w której dostępne będą wyłącznie zdefiniowane przez nas pola jest użycie slotów.

class Klasa(object):
    __slots__ = ['x', 'y']

if __name__ = '__name__':
    k = Klasa()
    k.x = 1
    k.y = 2
    k.a = 3
Traceback (most recent call last):
File "py.py", line 8, in ?
k.a = 3
AttributeError: 'Klasa' object has no attribute 'a'

Jak widać możemy przypisać wartości wyłącznie parametrom zdefiniowanym w __slots__.

http://docs.python.org/reference/datamodel.html#__slots__

Saturday, January 10, 2009

Python - kontrola dostępu do parametrów obiektu i klasy

O kontroli dostępu do składowych klasy pisałem już troszeczkę we wpisie Python - hermetyzacja i enkapsulacja z punktu widzenia programisty C++. Jest to jedna z metod kontroli dostępu jednak na małą skalę. Nadaje się świetnie do sprawowania pieczy nad dostępem do kilku pojedynczych atrybutów. W Pythonie istnieją jednak mechanizmy pozwalające na globalną kontrolę tego procesu. Zobaczmy jak to działa.

Python proponuje tutaj dwa podstawowe rodzaje metod. Pierwszą z nich, którą omówimy, będzie __setattr__(self, name, value). Metoda ta wywoływana jest za każdym razem gdy staramy się przypisać jakiemuś parametrowi klasy wartość. Żeby sprawdzić jak to działa spróbujmy następującego przykładu:
class Klasa(object):
    def __setattr__(self, name, value):
        print 'Ustawiasz atrybut ' + str(name) + ' na ' + str(value)

if __name__ == '__main__':
    k = Klasa()
    k.a = 1
    k.imie = 'Jan'
    k.ja = 'ty'
    k.liczba = 123.4
Za każdym razem gdy ustawiamy jakiś atrybut wywoływana jest nasza funkcja __setattr__ :) Fantastyczne narzędzie :) Problem polega jednak na tym, że w momencie, w którym nadpisaliśmy tą funkcję musimy sami zadbać o przypisanie odpowiednich wartości. Kiedy poznawałem opisywany mechanizm próbowałem ponownie wywołać funkcję __setattr__ dla klasy albo przypisać go wewnątrz tradycyjną metodą poprzez znak równości. Kończyło się to rekurencyjnym zapętleniem się wywołań metody __setattr__ a to z kolei błędem.

Metodą proponowaną w dokumentacjidokumentacji, dla new-style classes, a tylko o takich piszę, jest wywołanie tej metody na obiekcie bazowym. Tak więc aby nasza klasa rzeczywiście zapamiętywała przypisywane atrybutom wartości nasza metoda __setattr__ powinna wyglądać tak:
class Klasa(object):
    def __setattr__(self, name, value):
        print 'Ustawiasz atrybut ' + str(name) + ' na ' + str(value)
        object.__setattr__(self, name, value)
i wszystko będzie działać jak powinno. Co nam daje ta metoda ? Pozwala nam tak naprawdę na kontrolę tego co i w jaki sposób jest przypisywane do naszej klasy nawet w przypadku, w gdy przypisywany jest parametr, którego nie zdefiniowaliśmy (sic!). Moglibyśmy dzięki tej metodzie sprawdzać czy w naszej klasie istnieje atrybut, któremu wartość chce nadać programista i wyrzucić wyjątek gdy taki nie jest zdefiniowany. Bardzo elastyczne narzędzie.

Czas na pobieranie atrybutów. Początkowo w języku Python istniała metoda __getattr__(self, name). Była wywoływana za każdym razem gdy odwoływaliśmy się do nie istniejącego w klasie atrybutu i chcieliśmy jakoś na to zareagować, np. zwrócić wyjątek lub coś innego. Pokaże na przykładzie jak to wygląda.
class Klasa(object):
    a = None
    def __getattr__(self, name):
        print 'Atrybutu ' + name + ' nie ma w naszej klasie.'

if __name__ == '__main__':
    k = Klasa()
    k.a
    k.b
Program wpisze komunikat "Atrybutu b nie ma w naszej klasie.". I bardzo dobrze. Kiedy odwołaliśmy się do atrybutu, który nie istniej Python skorzystał z metody __getattr__(self, name). Metoda nie została wywołana przy atrybucie a gdyż został wcześniej zdefiniowany.

Gdybyśmy chcieli zabezpieczyć naszą klasę przed przypisywaniem wartości nasza metoda __getattr__ powinna zgłaszać wyjątek raise AttributeError :) W ten sposób próba dopisania do obiektu czegoś czego nie zdefiniowaliśmy kończyła by się błędem (wyjątkiem).

Tak było w Pythonie przed wersją 2.2. W wraz z wejściem new-style classes postanowiono wprowadzić metodę __getattribute__(self, name), która w przeciwieństwie do __getattr__ zadziała zawsze (zarówno gdy atrybut, który chcemy pobrać istnieje jak również gdy nie istnieje). Zapożyczę przykład fofoo z jednego z wątków z forum PPCG w którym pytałem o dokładniejsze wyjaśnienie tego mechanizmu.
class C(object):
    x = None
    def __getattr__(self, name):
        print '__getattr__', name
        #raise AttributeError
    def __getattribute__(self, name):
        print '__getattribute__',name
        if name == 'whoops':
            raise AttributeError
        return object.__getattribute__(self, name)
    def metoda(self): pass

if __name__ == '__main__':
    c = C()
    c.x
    print
    c.metoda()
    print
    c.a
    print
    c.whoops
Metoda __getattribute__ wywoła się za każdym razem :) Ponieważ czasami atrybut istnieje i chcemy uzyskać do niego dostęp musimy na końcu zwrócić object.__getattribute__(self, name). W tym momencie Python da nam dostęp do elementu, który wskazaliśmy. Jeżeli jednak nie istnieje sterowanie zostanie przekazane do metody __getattr__. Metoda __getattr__ zostanie również wywołana, jeżeli w __getattribute__ zgłosimy wyjątek AttributeError :)

Aby dopełnić informacje na koniec należałoby dodać iż z metod takich jak __getattribute__ czy __getattr__ oraz __setattr__ do parametrów można dobierać się w bezpieczny sposób (nie powodujący rekurencyjnego wywołania tych funkcji) również poprzez słowniki. Dla przykładu.
class Klasa(object):
    a = 'a1'
    def __init__(self):
        self.b = 'b1'

if __name__ == '__main__':
    k = Klasa()
    print k.__dict__['b']
    print type(k).__dict__['a']
Nasza klasa posiada parametr klasowy a i parametr instancji b. Oba są trzymane w słownikach. Aby dobrać się do słownika konkretnego obiektu wystarczy użyć k.__dict__, dobranie się do słownika samej klasy wymaga użycia type(k).__dict__ :)

Informacje w dokumentacji dotyczące tematu oraz jego rozwinięcie znajdziecie pod adresami:
http://docs.python.org/reference/datamodel.html#customizing-attribute-access

Wednesday, January 7, 2009

Python - pierwszy parametr metody to obiekt i co z tego wynika

Kiedy definiujemy jakąś metodę, nie statyczną, w języku Python jako pierwszy parametr każdej metody podajemy obiekt klasy (zwyczajowo nazywamy tę zmienną self).

No tak. Działa to jak "this" z C++ no i pojawia się to dziwne self w deklaracji metody jednak - co z tego dla nas wynika ? Otóż pewna piękna rzecz :) Możemy wywołać metodę na klasie i podać obiekt, na której ma się wywołać. Wygląda to na przykład tak.
class Samochod(object):
    def dzwiek(self):
        print 'brum'

if __name__ == '__main__':
    s = Samochod()
    Samochod.dzwiek(s)
Jak widać stworzyliśmy obiekt i przekazaliśmy go jako pierwszy parametr do metody wywoływanej na klasie :) Ktoś powie "dziwne". I rzeczywiście dziwne. Sprawa zaczyna nabierać jednak nabierać sensu rumieńców w kilku przypadkach. Jednym z nich jest sytuacja gdy nasza klasa dziedziczy z wielu klas i chociaż dwie z nich mają metodę o tej samej nazwie. Wyobraź sobie jak mogłaby wyglądać amfibia: jedź jak samochód i pływa jak łódź. Umówmy się, że samochód wydaje dźwięk brum a łódź plum. Spróbujemy to jakoś ubrać w klasy. Oto moja propozycja.
class Samochod(object):
    def dzwiek(self):
        print 'brum'

class Lodz(object);
    def dzwiek(self):
        print 'plum'

class Amfibia(Samochod, Lodz):
    pass
Jak widać klasa Amfibia dziedziczy zarówno po Samochod jak i po Lodz :) i pięknie. Jak myślisz ? jaki dźwięk wyda amfibia gdy wywołamy na niej tą metodę ?

a = Amfibia()
a.dzwiek()
brum

Ale dlaczego akurat brum ? Odpowiedź jest bardzo prosta. Amfibia w kolejności dziedziczy najpierw po samochodzie później po łodzi. Zostanie wzięta funkcja klasy pierwszej od lewej. Możesz to łatwo sprawdzić definiując klasę Amfibia jako class Amfibia(Lodz, Samochod) :) teraz dźwięk będzie dźwiękiem łodzi.

Pozostańmy przy amfibii, która najpierw dziedziczy po samochodzie class Amfibia(Lodz, Samochod). O ile wydanie dźwięku samochodu jak widać nie sprawia problemu o tyle, nie za bardzo jest możliwość, w tradycyjny sposób, dostać się do dźwięku łodzi. Otóż gdy wywołasz z amfibii dźwięk "potomka" wywoła się dźwięk samochodu, gdyby zaś w procedurze samochodu został wywołany dźwięk potomka to wywołałby się dźwięk łodzi. Coś w tym stylu.
class Samochod(object):
    def dzwiek(self):
        print 'brum'
        super(Samochod, self).dzwiek()

class Lodz(object);
    def dzwiek(self):
        print 'plum'

class Amfibia(Samochod, Lodz):
    def dzwiek(self):
        super(Amfibia, self).dzwiek()

if __name__ == '__main__':
    a = Amfibia()
        a.dzwiek()
Metoda dźwięk z Amfibii odwołuje się do metody dźwięk pierwszego (od lewej) potomka - czyli u nas samochodu. Wywoływana jest metoda dźwięk z samochodu.
W metodzie samochodu znowu pobierany jest potomek. Samochód sam w sobie nie ma potomka, ale wywołujemy go "z amfibii" jest więc pobierany kolejny potomek (tutaj łódź) i wywoływany jest dla niego metoda dźwięk().

Z naszego punktu widzenia wielodziedziczenie działa tak: amfibia (klasa) dziedziczy po samochodzie i po łodzi. Już wiemy, że potomkiem amfibii jest pierwsza klasa od lewej wymieniona w dziedziczeniu (u nas klasa Samochod), a kolejnym potomkiem (potomkiem szukanym w metodzie klasy samochód) jest łódź. "Czyli co ?" ktoś zapyta "Amfibia dziedziczy po samochodzie, który dziedziczy po łodzi ?". Tak to właśnie wygląda od strony wywoływania metod. Aby dostać się do dźwięku łodzi trzeba go wywołać w samochodzie.

Jak dobrze pomyśleć to wydaje się to straszne. Mając taki kod kiedy teraz zrobimy:
s = Samochod()
s.dzwiek()
To wyda się dźwięk samochodu i będzie błąd ponieważ metoda super próbuje znaleźć potomka samochodu (jedynie object) i wydać na nim dźwięk - co jest niemożliwe. Potomek samochodu ma sens tylko w przypadku użycia amfibii. Tak więc klops. Klasa samochód sama w sobie, tak skonstruowana, będzie powodowała błędy.

Jeżeli byliście uważnymi czytelnikami użyłem na początku sformułowania "w tradycyjny sposób". To znaczy, że Python pomimo takiej swojej śmiesznej metody wielodziedziczenia daje mechanizmy aby zrobić klasę samochód i łódź takie aby działały samodzielnie, oraz amfibię w której będzie można wydać oba dźwięki. Cała sztuczka polega właśnie na tym :) że jako pierwszy parametr metody podajemy obiekt, na którym ma się wywołać.

Zdefiniujmy na początku samochód i łódź - najnormalniej na świecie:
class Samochod(object):
    def dzwiek(self):
        print 'brum'

class Lodz(object);
    def dzwiek(self):
        print 'plum'
Bardzo proste. Obydwie mogą wydać dźwięk i widzimy, że nie odwołują się do żadnych metod "potomków" więc mogą działać sobie naprawdę niezależnie. Dobrze. Troszkę zmian będzie w amfibii. Teraz nasza amfibia będzie wydawała dwa dźwięki jednocześnie. Jak to zrobimy. Otóż - Wywołamy metodę dźwiek na klasie (nie na obiekcie) Samochod i klasie (nie na obiekcie) Lodz podając jako obiekt, na którym należy wywołać metodę, amfibię. Wszystko dzięki temu iż składnia Python wymusza na nas aby pierwszy parametr każdej metody, nie statycznej, był obiekt na którym ma działać nasza metoda. Wszystko co opisaliśmy powyżej wygląda dokładnie tak:
class Amfibia(Samochod, Lodz):
    def dzwiek(self):
        Samochod.dzwiek(self)
        Lodz.dzwiek(self)

if __name__ == '__main__':
    a = Amfibia()
    a.dzwiek()
Co się tu dzieje ? Ponieważ już wiemy, że metodą super z amfibii dostaniemy się do pierwszej (od lewej) klasy po której dziedziczy nasza, użyliśmy innego zabiegu. Wywołaliśmy metodę dźwięk na klasie Samochod. Jednak ponieważ metoda ta nie jest statyczna i trzeba ją wywołać na obiekcie jako pierwszy parametr podaliśmy obiekt, na którym operacja ma się wykonać - self czy w tym przypadku naszą amfibię. To samo zrobiliśmy dla łodzi. Ponieważ amfibia dziedziczy i po samochodzie i po łodzi będzie dobrze zachowywała się zarówno z metodami pierwszej jak i drugiej klasy.

Taka składnia wywołania to jeden z wielu smaczków Python-a wynikającej z troistości jego natury: strukturalne, obiektowej i tej którą widać tutaj - funkcjonalnej :] Nie oceniam czy to jak rozwiązane jest w Pythonie sprawdza się czy nie - czas pokaże.

Uwaga !
Użycie metody print wewnątrz klas jest zabiegiem upraszczającym zagadnienie mającym na celu ułatwienie zrozumienia problemu i kodu w celu zwiększenia jego walorów dydaktycznych. Autor świadomie pogwałcił zasady dotyczących I/O klasy i obiektów w celu IMHO uczynienia przykładu czytelniejszym i przejrzystszym.

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 !

Klasa opakowująca sockety w C++

Dziś udało mi się w końcu :] ukończyć ładne opakowanie socketów UNIXowych w klasy :)
Niestety nie jest to "międzyplatformowe" i wysyłanie wiadomości nie będzie działało na platformach, na których int ma różne rozmiary :)
Ale ponieważ i tak zakłada działanie lokalne więc jest dobrze. Wyniki można obejrzeć tutaj.

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).