Zanurkuj w Pythonie/Definiowanie klas

Definiowanie klas

edytuj

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. KazdeSlowoOddzieloneWielkaLitera, 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 niewcięta instrukcja nie będzie należała już do klasy.

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.

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

Inicjalizowanie i implementowanie klasy

edytuj

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, nie musimy go podawać 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ą.
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.

Kiedy używać self, a także __init__

edytuj

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

Przypisy

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