Podsumowanie¶
W części (Niezbyt) oczywiste elementy Pythona pojawił się przykład process_files.py, w którym de facto wykorzystano wzorzec Dyspozytor (Dispatch Pattern). W tym przypadku jednak kluczami słownika były napisy, wartościami zaś - funkcje! Czy obiekty rzeczywiście są takie przydatne? W pomiższym przykładzie
1# ---------- The version with functions (no objects at all) ---------- 2def woof(): 3 print("woof") 4 5 6def purr(): 7 print("purr") 8 9 10say_sth = {"dog": woof, "cat": purr} 11 12for animal in ["dog", "dog", "cat"]: 13 say_sth[animal]() 14 15 16# ---------- The version with objects ---------- 17 18class Woof: 19 def __call__(self): 20 print("woof, woof") 21 22 23class Purr: 24 def __call__(self): 25 print("purr, purr") 26 27 28say_sth = {"dog": Woof(), "cat": Purr()} 29 30for animal in ["dog", "dog", "cat", "dog"]: 31 say_sth[animal]()
wykorzystano obydwa podejścia. Zauważ, że samo wykorzystanie dyspozytora opartego na obiektach (linie 30-31) jest identyczne co w wariancie z funkcjami (linie 12-13). Pisanie klas wydaje się zbędnym wysiłkiem. A jak by można było rozszerzyć funkcjonalność dyspozytora, np. tak, aby zliczał ile razy odezwało się każde ze zwierząt? Dzięki wykorzystaniu obiektów rozwiązanie jest bardzo proste:
1class Speak: 2 def __init__(self): 3 self.n = 0 4 5 6class Woof(Speak): 7 def __call__(self): 8 self.n += 1 9 print("woof, woof", self.n) 10 11 12class Purr(Speak): 13 def __call__(self): 14 self.n += 1 15 print("purr, purr", self.n) 16 17 18say_sth = {"dog": Woof(), "cat": Purr(), "retriever": Woof()} 19 20for animal in ["dog", "cat", "cat", "retriever"]: 21 say_sth[animal]()
Zauważ, że retriever też szczeka, ale jest liczony oddzielnie niż inne psy!
Uważny czytelnik może zauwazy, że problem zliczania, ile razy została wywołana każda z funkcji, można rozwiązać stosując dekoratory funkcji. W takim przypadku jednak trzeba by owe liczniki zapisać jako zmienne globalne. Programowanie obiektowe umożliwia nam schowanie ich we wnętrzu obiektów.
Lektura uzupełniająca¶
https://rszalski.github.io/magicmethods/
Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, ,,Wzorce projektowe. Elementy oprogramowania obiektowego wielokrotnego użytku’’
https://python-3-patterns-idioms-test.readthedocs.io/en/latest/PythonDecorators.html
Spis pojęć¶
- bazowa klasa¶
klasa po której inna klasa dziedziczy
- chroniona składowa¶
pole bądź metoda klasy, widoczna tylko dla klas potomnych
- delegacja¶
wzorzec projektowy
- destruktor¶
metoda wywoływana automatycznie (i dokładnie raz) w momencie niszczenia obiektu; w Pythonie destruktor deklarujemy jako
__del__():def __del__(self) : # Zrob coś na samym koncu, np zamknij plik
- enkapsulacja¶
ukrywanie implementacji; zapewnia ona, że obiekt nie może zmienić nieoczekiwanie stanu innych obiektów. Stan obiektu mogą zmienić jedynie jego metody publiczne, czyli interfejs. Enkapsulacja ułatwia testowanie i pielęgnację (maintenance) kodu.
- fabryka klas¶
wzorzec projektowy
- getter¶
metoda udostępniająca prywatną składową klasy; w Pythonie można ją zadeklarować korzystając z dekoratora
@property:@property def val(self) : return self.__val # Zwraca prywatną składową ``__val``
- interfejs¶
zbiór metod publicznych klasy
- iterable¶
obiekt jest iterable, jeżeli umie zwrócić iterator do swojej zawartości; w praktyce jest to coś, co można wpisać w pętlę
for:for l in coś : pass
Aby klasa w Pythonie była iterable, musi implementować magiczną metodę
__iter__()- iterator¶
obiekt, który umie zwrócić następny element w sekwencji (posiada metodę
__next__())- klasa¶
definicja obiektu; definiuje jego wszystkie pola i metody
- klasa abstrakcyjna¶
klasa, która posiada choć jedną metodę abstraktyjną; w Pythonie klasy takie muszą dziedziczyć po klasie bazowej
ABCzdefiniowanej w moduleabc- konstruktor¶
metoda wywoływana automatycznie (i dokładnie raz) w momencie tworzenia obiektu; w Pythonie destruktor konstruktor jako
__init__():def __init__(self) : # Zainicjuj wszystkie składowe klasy
- konstruktor domyślny¶
konstruktor, który nie przyjmuje żadnych parametrów; ewentualnie korzysta z domyślnych wartości
- konstruktor kopiujący¶
konstruktor, który tworzy głęboką kopię obiektu; nowo utworzony obiekt jest identyczny z tym, który był argumentem konstruktora
- magic functions¶
TBD
- metoda¶
funkcja zdefiniowana jako składowa klasy
- metoda abstrakcyjna¶
metoda, która została jedynie zadeklarowana, lecz nie zaimplementowana; metoda taka musi być zaimplementowana w klasie potomnej. W Pythonie metody takie deklarujemy korzystając z dekoratora
@abstractmethod- nadklasa¶
dowolna klasa, po której dana klasa dziedziczy (bezpośrednio lub nie)
- obiekt (instancja klasy)¶
struktura, która może zawierać w sobie funkcje (metody) oraz zmienne (pola)
- operator wywołania¶
operator zapisywany jako nawiasy zwykłe, czyli
(); w Pythonie dodajemy go do klasy implementując w niej magiczną metodę__call__()- podklasa¶
synonim do klasy potomnej
- pole (atrybut) klasy¶
zmienna zadeklarowana wewnątrz klasy
- polimorfizm¶
obiekty tego samego typu (klasy bazowej) zachowują się różnie
- potomna klasa¶
klasa, która dziedziczy po jakiejś klasie (zwanej klasą bazową)
- prywatna składowa¶
pole bądź metoda klasy, niewidoczna z zewnątrz klasy; jedynie składowe klasy mogą korzystać z prywatnych składowych klasy, do której należą
- przeciążenie¶
klasa ma kilka metod o takiej samej nazwie lecz różnych argumentach; w Pythonie realizowane przez
*args- przesłanianie¶
klasa potomna ma metodę o takiej samej nazwie i argumentach, co klasa bazowa, lecz o zmienionym zachowaniu
- publiczna składowa¶
pole bądź metoda klasy widoczna dla wszystkich innych obiektów, metod i funkcji; w każdym miejscu kodu można odczytać bądź zmienić jej wartość
- singleton¶
wzorzec projektowy w którym możliwe jest stworzenie tylko jednego obiektu danej klasy
- statyczne składowe¶
składowe klasy, które nie są związane z żadnym konkretnym obiektem klasy; istnieją nawet wtedy, kiedy jeszcze nie stworzono żadnego obiektu tej klasy; mogą być prywatne lub publiczne