Saturday, January 24, 2009

Sprostowanie co do mojego merbowania

Pisałem jakiś czas temu (dawno) na moim blogu o Merbie, zachwycałem się HAMLem i SASSem (bo naprawdę byłem pod ich wrażeniem). Później zacząłem krytykować Railsy za ich kody i wykazywałem totalne niezrozumienie i krytykę wobec osób, które nadal programowały w Railsach.

W Merbie nie napisałem ani jednej aplikacji. Robiłem tyle co pisałem na Blogu. Generalnie templaty: HAML/SASS i odbieranie jakiś danych w kontrolerze. Na modele byłem zawsze za leniwy. Jeżeli ktokolwiek poczuł się urażony moją ostrą krytyką - to przepraszam. Niestety mój "perfekcjonizm" programisty dał mi się we znaki i zamiast krytykować Railsy zacząłem jak widzę krytykować osoby.

Do kodu Railsów nigdy nie zaglądałem i moje zdanie jest zdaniem wyrobionym na bazie idei obu projektów, celów im przyświecających oraz opiniom rzeczy osób programujący w Rails i Merb, których blogi czytam. Czepiam się nie funkcjonalności a jakości kodu, który w Rails jest podobno słabej jakości.

W Rails robiłem kiedyś jakiś tutorial oparty na scaffoldingu i generowaniu kodu :]

Urażonych przepraszam :) Mam nadzieję, że sprawę wyjaśniłem dość jasno. W Merbie i w Rails nie pisałem żadych dużych projektów. Bawiłem się nimi tylko jako frameworkami, gdzie w Merbie robiłem to tylko na bazie wysyłania formularzy i bawienia się HAML i SASSem a w Rails jakimś tutorialem w stylu "scaffolding" czy jakieś magiczne automaty :)

Friday, January 23, 2009

Django banał

Dziś pisałem przykładową aplikację, na której będę chciał pokazać jak wykorzystuje się luki w atakach XSS i CSRF. Chciałem to zrobić szybko i wybrałem Django, którego w ogóle nie znam.

Muszę przyznać, że jestem w szoku. Aplikację napisałem w trymiga, dokumentacja świetna, walidacja, formularze generują się same a modele posiadają metody idealnie do CRUDa. Najbardziej denerwują urls.py, które co nóż to trzeba uzupełniać. Formularze tworzyłem na bazie modeli i nie za bardzo wiedziałem też jak zrobić bez grzebania w klasie formularza (o ile to w ogóle możliwe), żeby pewne pole wyświetlało się w trybie readonly. Poza tym wszystko szło jak burza.

Szczerze mówiąc framework szalenie przyśpiesza tworzenie aplikacji. Dzięki temu, że nie trzeba dokładać do niego kolejnych modułów skupiam się na pisaniu aplikacji nie zaś na szukaniu rozwiązań, które po prostu były podane jak na dłoni w świetnej moim zdaniem dokumentacji.

Programowało mi się przez tą chwilę znacznie lepiej, szybciej, wygodniej i przyjemniej niż w Pylons, w którym wszystko musiałem robić sam. W Django nie widać na przykład żadnych transakcji (jeżeli nie chce się ich mieć), klasy posiadają metody - nie trzeba korzystać jawnie z jakiegoś meta.Session, gdzie w Pylons musisz sobie samemu (choć to tylko chwilka) zrobić takie cudo.

Brakowało mi tylko dekoratora do walidacji formularzy, żeby wyrzucić oczywisty kod na zewnątrz metody i nie zaśmiecać jej sprawdzaniem czy formularz się zwalidował ale napisanie go to pewnie sekundka.

Nie będę się jednak uczył tego frameworka - na ile mi potrzeba już go umiem :] Po Pylons (gdy skończę pisać projekt, który w nim zacząłem) przyjdzie czas na naukę Rubego :P

Thursday, January 22, 2009

TruboGears 2 - framework na Pylons

Wiadomość stara - jednak dla mnie szokująca. TurboGears w wersji 2 to framework, który jest napisany na Pylons. Nie wiem czy istnieje inny przykład frameworka pisanego na innym frameworku. Wątek na pylons-discuss jest z 27 Czerwca 2007 r.

Zainteresowałem się temat ponieważ dziwnie często w niektórych źródłach ukazywały się jakieś "porównania" tych frameworków, ale nie na zasadzie TurboGears vs Pylons tylko na zasadzie "co użyto w Pylons co można byłoby użyć w TG", albo "W Pylons użyte jest to a w TG użyto tego i tego - trzeba sprawdzić czy nie powinniśmy używać tego w Pylons". Moim skromnym zdaniem :) fakt zaistnienia takiej sytuacji pokazuje, że Pylons to poważny framework, ceniony w gronie developerów Pythona :] i to aż tak fajny, że można na nim pisać inne frameworki.

TG 2.0 jeszcze nie wyszedł (jest w wersji beta), być może wydanie jego pełnej wersji jest uzależnione od wyjścia Pylons w wersji 1.0. Poczekamy - zobaczymy :)

Nowa strona Pylons i wersja 0.9.7 rc4

Od wczoraj na stronie Pylons "psuło się" w niektórych działach menu - dziś możemy oglądać nową odsłonę witryny. Wiele witryn ładnie komponuje się z "otoczką" w stylu egipskim - najlepiej witryna główna. Wiki straszy obecnie troszeczkę Python stylowym wyglądem, który ciężko przypiąć do obecnego layoutu witryny :]

Myślę, że wraz z odświeżeniem strony nastąpi też ożywienie społeczności, będzie to jaki bodziec, pozytywny akcent. To co cieszy to fakt iż proponowana do ściągnięcia na stronie wersja Pylons to 0.9.7rc4. Jak można wyczytać na grupie dyskusyjnej do wydania wersji 0.9.7 kod prawdopodobnie nie ulegnie zmianie bo jedyne zadania, które zostały do ukończenia to tickety niezwiązane z kodem.

Jestem szalenie ciekaw jak szybko ruszą prace nad 9.8 i czy zmiany z 9.7 zostaną zmergowane do gałęzi 1.0, która już od około 10 miesięcy się nie zmienia. Zobaczymy :) Czekam na marcową wersję Pylons 1.0 ;]

Wednesday, January 21, 2009

Python - enumerate i reversed jako bonusy do fora

Pętlę for w Pythonie można wzbogacić używając dwóch ciekawych funkcji.

Pierwszą z nich, którą omówię będzie enumerate.

krotka = ('a', 'b', 'c', 'd')

for nr, element in enumerate(krotka):
    print nr, " ", element
Jak widzimy dzięki funkcji enumerate poza elementem kroki zapisywanym w drugiej zmiennej dostajemy w zmiennej nr kolejne liczby (licząc od zera). Dzięki temu dostajemy "typowy" licznik, który zwiększa swoją wartość wraz z kolejnym obrotem pętli.

Dzięki funkcji reversed możemy zaś iterować po elementach w odwrotnej kolejności


krotka = ('a', 'b', 'c', 'd')

for element in reversed(krotka):
    print element
Kolejne elementy będą wyświetlane biorąc od końca krotki. Proste :)

Python - Conditional Expression

Python posiada składnię "conditional expression". Pamiętacie taką śmieszną strukturę (warunek) ? jezeli_true : jezeli_false ?

W Pythonie przyjmuje ona formę (na przykład):

'parzyste' if x % 2 == 0 else 'nieparzyste'

To co kole mnie w oczy to fakt iż po warunku na if nie ma dwukropka, ale odrazu else. Jak ta struktura działa ? Jeżeli warunek w ifie jest spełniony zostanie zwrócona wartość 'parzyste', jeżeli nie 'nieparzyste'. Zwróconą wartość można przyporządkować łatwo do zmiennej. Na przykład

liczba = 4
czy_parzyste = 'parzyste' if liczba % 4 == 0 else 'nieparzyste'
czy_parzyste = ('parzyste' if liczba % 4 == 0 else 'nieparzyste')


O dziwo zarówno pierwszy jak i drugi zapis działają tak samo. Zmienne czy_parzyste zostanie nada wartość 'parzyste' lub 'nieparzyste', przy czym IMHO drugi zapis jest o niebo czytelniejszy !

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 !

Projekty z WinAPI

Na siódmym semestrze informatyki męczą nas niestety z WinAPI.

Pierwszy programik ma za zadanie proporcjonalnie zmieniać rozmiary okna, pokazywać aktualne wymiary w pasku tytułowym, pytać o zamknięcie okna i przyciągać się do górnej i lewej krawędzi ekranu. Pisałem go go w DevC++

Drugi (pisany niestety w Visual Studio 2005) ma za zadanie pozwalać na rysowanie elipsy, która będzie dobrze zachowywała się przy wyjechaniu oknem poza ekran oraz przy rysowaniu jej kilka razy itd...

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