Zanurkuj w Pythonie/Definiowanie klas
Definiowanie klas
edytujPython 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.
class Nicosc(object): #(1)
pass #(2) (3)
- Nazwa tej klasy to
Nicosc
, a klasa ta dziedziczy z wbudowanej klasyobject
. Nazwy klas są zazwyczaj pisane przy użyciu wielkich liter np.KazdeSlowoOddzieloneWielkaLitera
, ale to kwestia konwencji nazewnictwa; nie jest to wymagane. - 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ą. - 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.
Uwaga!
|
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__.
FileInfo
class FileInfo(dict): #(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 klasydict
, a ta klasa, to po prostu klasa słownika.
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)
.
Inicjalizowanie i implementowanie klasy
edytujTen przykład przedstawia inicjalizację klasy FileInfo
za pomocą metody __init__
.
class FileInfo(dict):
u"przechowuje metadane pliku" #(1)
def __init__(self, filename=None): #(2) (3) (4)
- Klasy mogą (a nawet powinny) posiadać także notkę dokumentacyjną, podobnie jak moduły i funkcje.
- 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ę. - 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 nazywanyself
. 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ć argumentself
podczas definiowania metody, nie musimy go podawać w czasie wywoływania metody; Python dodaje go automatycznie. - 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 argumentfilename
ma domyślną wartość określoną jakoNone
, który jest Pythonową pustą wartością.
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.
|
FileInfo
class FileInfo(dict):
u"przechowuje metadane pliku"
def __init__(self, filename=None):
dict.__init__(self) #(1)
self["plik"] = filename #(2)
#(3)
- 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.
- 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. - 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].
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
- ↑ Wynika to z różnic pomiędzy metodami instancji klasy (ang. bound method), a metodami samej klasy (ang. unbound method)