Monday, December 22, 2008

Python - klasy z punktu widzenia programisty C++

Kiedy tworzyliśmy klasę w C lub C++ używaliśmy słowa kluczowego class lub struct (od tego zależał domyślny tryb widoczności dla elementów klasy). Trzeba było zapisać co jest private, co protected a co public. Czasami potrzebowaliśmy aby jakaś metoda lub atrybut klasy był statyczny. Do wszystkiego używaliśmy specjalnych słów kluczowych. Jeżeli chcieliśmy stworzyć konstruktor tworzyliśmy metodę o nazwie naszej klasy a destruktor - podobnie, tyle tylko, że nazwę tą poprzedzaliśmy tyldą.

Wszystko omówię na podstawie przykładowej klasy Pythona.

class MojaKlasa(object):
    zmienna_statyczna = 12

    def __init__(self):
        self.zwykla_zmienna = 'a'
        print 'Ja się wywołam przy toworzeniu'

    def __del__(self):
        print 'Ja się wywołam przy usuwaniu'

Powyższa klasa dziedziczy po object. Dalej w ciele klasy definiujemy zmienną o nazwie zmienna_statyczna i przypisujemy jej wartość 12. W języku Python wszystkie zmienne definiowane w ciele klasy zachowują się jak zmienne statyczne. Metoda __init__ (nazwa zaczyna się i kończy od dwóch podkreślników) wywoła się zaraz po utworzenia obiektu :) Zaś metoda __del__  podczas jego usuwania. Piszę celowo "wykona się zaraz po utworzeniu", a nie "jest konstruktorem" ponieważ jest to stwierdzenie bliższe prawdzie.

Metody statyczne oznaczamy podając przed ich deklaracją @staticmethod:

class Klasa(object):
    zmienna = 12

    @staticmethod
    def mojaStatycznaMetoda():
        return Klasa.zmienna
Metoda jak widać nie przyjmuje parametru self (nie miałoby to sensu skoro jest statyczna i może być wywołana dla klasy bez tworzenia jej instancji). Aby odwołać się w niej do zmiennej statycznej możemy użyć Klasa.zmienna gdzie klasa to nazwa klasy a zmienna to nazwa zmiennej zadeklarowanej w jej ciele. Podobnie wygląda wywołanie metody statycznej Klasa.mojaStatycznaMetoda().

Wszystkie metody przyjmują jako pierwszy parametr self, czyli "samego siebie". Jest to jawny zapis tego co w C++ jest ukryte w mechanizmach języka i traktowane "domyślnie" (wszak możesz w metodach klasy C++ odwoływać się do obiektu z użyciem słówka this chociaż nigdzie go nie przekazujesz). Wszystkie atrybuty zdefiniowane w metodzie __init__ z użuyciem self.nazwa_zmiennej są jak widać zmienny obiektu nie klasy co po przeanalizowaniu znaczenia zmiennej self (ja, moja instancja, aktualnie tworzony obiekt) ma rzeczywiście sens :) Zmienne należą do obiektu (self), są definiowane dla niego (konkretnej instancji) nie zaś w ciele samej klasy.

A co z atrybutami: prywatny, publiczny. Domyślnie wszystkie metody i atrybuty w Pythonie są publiczne. I tutaj, aby zdefiniować elementy prywatne, zrezygnowano ze słów kluczowych na rzecz konwencji. Aby element klasy był prywatny jego nazwa musi zaczynać się od dwóch podkreślników to jest od __.  Tak więc atrybuty:


class KolejnkaKlasa(object):
    __zmienna_statyczna
    def __init__(self):
        self.__top_secret

    def __nie_wywolasz_mnie(self):
        pass
    pass
Wszystko czego nazwa zaczyna się od dwóch podkreślników będzie prywatne, ze swojej natury.
Co można powiedzieć o wartościach protected ? Są - ale istnieją jedynie z umowy. Umownie - wszystko czego nazwa rozpoczyna się od jednego znaku podkreślenia jest parametrem chronionym. Umowanie - ponieważ faktycznie ma się do niego nadal publiczny dostęp. Czy to znaczy, że Python nie wspiera metod i atrybutów chronionych w klasach. Tak - w Pythonie nie zaimplementowano tego mechanizmu. Zrobiono to celowo. Zainteresowanych odsyłam do wątku "Protected Methods and Python" na grupie dyskusyjnej języka. Jak widać po dyskusji, ufając intuicji autorytetu jakim jest Bjarne StrouStrup (twórca języka C++) atrybty i metody chronione to zły, zbędny mechanizm :) Więc nie martw się nic nie tracisz :)

No comments: