Zanurkuj w Pythonie/Definiowanie klas

Z Wikibooks, biblioteki wolnych podręczników.

[edytuj] Definiowanie klas

Python jest całkowicie zorientowany obiektowo: możemy definiować własne klasy, dziedziczyć z własnych lub wbudowanych klas, a także tworzyć instancje zdefiniowanych przez siebie klas.

Tworzenie klas w Pythonie jest proste. Podobnie jak z funkcjami, nie używamy oddzielnego interfejsu definicji. Po prostu definiujemy klasę i zaczynamy ją implementować. Klasa w Pythonie rozpoczyna się słowem kluczowym class, po którym następuje nazwa klasy, a następnie w nawiasach okrągłych umieszczamy, z jakich klas dziedziczymy.

Przykład. Prosta klasa
class Nicosc(object):   #(1)
    pass                #(2) (3)

  1. Nazwa tej klasy to Nicosc, a klasa ta dziedziczy z wbudowanej klasy object. Nazwy klas są zazwyczaj pisane przy użyciu wielkich liter np. KazdeSlowoOdzieloneWielkaLitera, ale to kwestia konwencji nazewnictwa; nie jest to wymagane.
  2. Klasa ta nie definiuje żadnych metod i atrybutów, ale żeby kod był zgodny ze składnią Pythona, musimy coś umieścić w definicji, tak więc użyliśmy pass. Jest to zastrzeżone przez Pythona słowo, które mówi interpreterowi "przejdź dalej, nic tu nie ma". Instrukcja ta nie wykonuje żadnej operacji i powinniśmy stosować ją, gdy chcemy zostawić funkcję lub klasę pustą.
  3. Prawdopodobnie zauważyliśmy już, że elementy w klasie są wyszczególnione za pomocą wcięć, podobnie jak kod funkcji, instrukcji warunkowych, pętli itp. Pierwsza nie wcięta instrukcja nie będzie należała już do klasy.


Uwaga! Uwaga!
Od Pythona 2.2 wprowadzono nowy styl klas (ang. new-style classes), którego będziemy się uczyli. Aby pisać klasy w nowym stylu, musimy dziedziczyć je z którejś wbudowanej klasy, najczęściej będzie to object. W przeciwnym wypadku pisane przez nas klasy będą w starym stylu i niektóre możliwości nie będą dostępne. Dzięki takiemu zachowaniu Pythona, jest on kompatybilny wstecz.
Porada Instrukcja pass w Pythonie jest analogiczna do pustego zbioru nawiasów klamrowych ({}) w Javie lub w języku C++.


W prawdziwym świecie większość klas definiuje własne metody i atrybuty. Jednak, jak można zobaczyć wyżej, definicja klasy, oprócz swojej nazwy z dodatkiem object w nawiasach, nie musi nic zawierać. Programiści C++ mogą zauważyć, że w Pythonie klasy nie mają wyraźnie sprecyzowanych konstruktorów i destruktorów. Pythonowe klasy mają coś, co przypomina konstruktor -- metodę __init__.


Przykład. Definiowanie klasy FileInfo
class FileInfo(dict): #(1)
  1. Jak już wiemy, klasy, z których chcemy dziedziczyć wyszczególniamy w nawiasach okrągłych, które z kolei umieszczamy bezpośrednio po nazwie naszej klasy. Tak więc klasa FileInfo dziedziczy z wbudowanej klasy dict, a ta klasa, to po prostu klasa słownika.


Porada W Pythonie klasę, z której dziedziczymy, wyszczególniamy w nawiasach okrągłych, umieszczonych bezpośrednio po nazwie naszej klasy. Python nie posiada specjalnego słowa kluczowego jak np. extends w Javie.


Python obsługuje dziedziczenie wielokrotne. Wystarczy w nawiasach okrągłych, umiejscowionych zaraz po nazwie klasy, wstawić nazwy klas, z których chcemy dziedziczyć i oddzielić je przecinkami np. class klasa(klasa1,klasa2).

[edytuj] Inicjalizowanie i implementowanie klasy

Ten przykład przedstawia inicjalizację klasy FileInfo za pomocą metody __init__.

Przykład. Inicjalizator klasy FileInfo
class FileInfo(dict):
    u"przechowuje metadane pliku"        #(1)
    def __init__(self, filename=None):   #(2) (3) (4)
  1. Klasy mogą (a nawet powinny) posiadać także notkę dokumentacyjną, podobnie jak moduły i funkcje.
  2. Metoda __init__ jest wywoływana bezpośrednio po utworzeniu instancji klasy. Może kusić, aby nazwać ją konstruktorem klasy, co jednak nie jest prawdą. Metoda __init__ wygląda podobnie do konstruktora (z reguły __init__ jest pierwszą metodą definiowaną dla klasy), działa podobnie (jest pierwszym fragmentem kodu wykonywanego w nowo utworzonej instancji klasy), a nawet podobnie brzmi (słowo "init" sugeruje, że jest to konstruktor). Niestety nie jest to prawda, ponieważ obiekt jest już utworzony przed wywołaniem metody __init__, a my już otrzymujemy poprawną referencję do świeżo utworzonego obiektu. Jednak __init__ w Pythonie, jest tym co najbardziej przypomina konstruktor, a ponadto pełni prawie taką samą rolę.
  3. Pierwszym argumentem każdej metody znajdującej się w klasie, włączając w to __init__, jest zawsze referencja do bieżącej instancji naszej klasy. Według powszechnej konwencji, argument ten jest zawsze nazywany self. W metodzie __init__ self odwołuje się do właśnie utworzonego obiektu; w innych metodach klasy, self odwołuje się do instancji, z której wywołaliśmy daną metodę. Mimo, że musimy wyraźnie określić argument self podczas definiowania metody, ale nie określamy go w czasie wywoływania metody; Python dodaje go automatycznie.
  4. Metoda __init__ może posiadać dowolną liczbę argumentów i podobnie jak w funkcjach, argumenty mogą być zdefiniowane z domyślnymi wartościami (w ten sposób stają się argumentami opcjonalnymi). W tym przypadku argument filename ma domyślną wartość określoną jako None, który jest Pythonową pustą wartością.


Porada Według konwencji, pierwszy argument metody należącej do pewnej klasy (referencja do bieżącej instancji klasy) jest nazywany self. Argument ten pełni tą samą rolę, co zastrzeżone słowo this w C++ czy Javie, ale self nie jest zastrzeżonym słowem w Pythonie, jest jedynie konwencją nazewnictwa. Niemniej lepiej pozostać przy tej konwencji, ponieważ jest to wręcz bardzo silna umowa.


Przykład. Kodowanie klasy FileInfo
class FileInfo(dict):
   u"przechowuje metadane pliku"
   def __init__(self, filename=None):
       dict.__init__(self)             #(1)
       self["plik"] = filename         #(2)
                                       #(3)
  1. Niektóre języki pseudo-zorientowane obiektowo jak Powerbuilder posiadają koncepcję "rozszerzania" konstruktorów i innych zdarzeń, w których metoda należąca do nadklasy jest wykonywana automatycznie przed metodą podklasy. Python takiego czegoś nie wykonuje; zawsze należy wyraźnie wywołać odpowiednią metodę należącą do przodka klasy.
  2. Klasa ta działa podobnie jak słownik (w końcu z niego dziedziczymy), co mogliśmy zauważyć po spojrzeniu na tę linię. Przypisaliśmy argument filename jako wartość klucza "plik" w naszym obiekcie.
  3. Zauważmy, że metoda __init__ nigdy nie zwraca żadnej wartości.


[edytuj] Kiedy używać self i __init__

Podczas definiowania metody pewnej klasy, musimy wyraźnie wstawić self jako pierwszy argument każdej metody, włączając w to __init__. Kiedy wywołujemy metodę z klasy nadrzędnej, musimy dołączyć argument self, ale jeśli wywołujemy metodę z zewnątrz, nie określamy argumentu self, po prostu go pomijamy. Python automatycznie wstawi odpowiednią referencję za nas. Na początku może się to wydawać trochę namieszane, jednak wynika to z pewnych różnic, o których jeszcze nie wiemy [1].

Porada

Metoda __init__ jest opcjonalna, ale jeśli ją definiujemy, musimy pamiętać o wywołaniu metody __init__, która należy do przodka klasy. W szczególności, jeśli potomek chce poszerzyć pewne zachowanie przodka, dana metoda podklasy musi w odpowiednim miejscu bezpośrednio wywoływać metodę należącą do klasy nadrzędnej, oczywiście z odpowiednimi argumentami.


Przypisy


  1. Wynika to z różnic pomiędzy metodami instancji klasy (ang. bound method), a metodami samej klasy (ang. unbound method)