Na co nam te obiekty ?
Czy można programować w Pythonie nie używając obiektów ? Oczywiście, że tak tylko po co. Pomijając paru programistycznych nooobów od języka C większość programistów traktuje obiektowość jak błogosławieństwo.
Posługujemy się obiektami, bo tak jest nam wygodniej. Dodatkowo aplikacje napisane obiektowo są łatwiejsze w naprawie, rozwijaniu oraz zarządzaniu. Są modularne, łatwiej przystosowywać je do nowych wytycznych, a także łatwiej je testować co pozytywnie wpływa na jakość pisanego przez nas oprogramowania. Więcej na temat unit testów napisze w oddzielnym wpisie. Dzisiaj zajmiemy się tworzeniem swoich własnych typów czyli właśnie klas, a na ich podstawie już konkretnych obiektów.
Wszytko jest obiektem
Jeśli myślisz, że jeszcze nigdy nie programowałeś obiektowo to jestem prawie pewny, że się mylisz. W Pythonie wszytko jest obiektem. Jeśli kiedykolwiek zrobiłeś zmienną trzymająca wartość liczbową, czy też tekstową to właśnie stworzyłeś obiekt.
moja_liczba = 32 print(type(moja_liczba))
W pierwszej linii tworzymy zmienną o nazwie moja_liczba i przypisujemy do niej wartość 32. W drugiej linii wyświetlamy typ stworzonej przez nas zmiennej. Program wyświetli nam coś takiego:
<class 'int'>
Nasza zmienna moja_liczba jest typu int.
Dlaczego to działa ? Dlaczego udało nam się stworzyć instancję zmiennej typu, którego sami nie zrobiliśmy ? To dlatego, że Python ma już kilka typów wbudowanych. Są nimi np.: liczby, teksty, listy, krotki, słowniki.
moja_liczba = 32 # <class 'int'> print(type(moja_liczba)) druga_liczba = 22.0 # <class 'float'> print(type(drug_liczba)) moj_tekst = 'tarpan' # <class 'str'> print(type(moj_tekst)) moja_lista = [1, 2, 3] # <class 'list'> print(type(moja_lista)) moja_krotka = (1, 2, 3) # <class 'tuple'> print(type(moja_krotka))
Zatem aby stworzyć obiekt, najpierw potrzebujemy typu. Typ jest swego rodzaju formą, instrukcją, przepisem na stworzenie instancji klasy. Możemy tak jak to zrobiliśmy wcześniej skorzystać z typów wbudowanych (predefiniowanych) lub też przygotować nasz własny typ. Tworzenie klas to właśnie tworzenie własnych typów.
Nazewnictwo
klasa = typ
Pisząc nową klasę robimy nowy typ.
instancja klasy = obiekt
Konstruując nowy obiekt tworzymy instancję klasy.
Nasza pierwsza klasa
By wyprodukować instancję naszej pierwszej klasy musimy stworzyć własny typ, formę, przepis na robienie obiektów. W Pythonie jest to raczej proste. Oto minimalna klasa:
class Auto: pass #pass nie robi nic jednak jest wymagane by stworzyć pustą klasę
Właśnie zrobiliśmy pierwszą klasę, czyli stworzyliśmy własny typ. Od tej pory poza typami wbudowanymi jak np: ”int” czy „str” możemy również używać naszego typu Auto. Oto mały programik pokazujący stworzenie dwóch obiektów typu Auto:
class Auto: pass tarpan = Auto() # tworzymy obiekt auto i przypisujemy go do zmiennej o nazwie tarpan polonez = Auto() # tworzymy obiekt auto i przypisujemy go do zmiennej nazwie polonez # od tej chwili mamy w naszym programie dwie instancje klasy czyli obiekty powstałe z naszej klasy Auto print(type(tarpan)) # wyświetlamy typ zmiennej tarpan print(type(polonez)) # wyświetlamy typ zmiennej polonez
Jak pewnie zauważyłeś obiekty typu Auto stworzyliśmy w troszkę inny sposób niż robiliśmy to ze zmiennymi typów wbudowanych:
polonez = Auto() vs tekst = 'jakiś tekst'
To dlatego, że Python dla typów wbudowanych automatycznie rozpoznaje typ.
Nic jednak nie stoi na przeszkodzie by zmienną tekst stworzyć w sposób bardziej jawny:
tekst = str('jakiś tekst') #tworzę obiekt typu str i przypisuję do zmiennej tekst liczba = int(2) # tworzę obiekt typu int i przypisuję do zmiennej liczba oraz ustawiam ją na wartość 2
Teraz wygląda to prawie tak samo jak tworzenie obiektu typu Auto.
Metody klasy
No super udało nam się stworzyć naszą pierwszą klasę (typ) o nazwie ”Auto”. Problem z tym, że na niewiele przyda nam się pusta klasa. Co powiecie na dodanie do niej paru metod ? Tylko czym są te metody ? Metody to nic innego jak funkcje wewnątrz klasy. Symbolizują one akcje jakie nasz obiekt może wykonać. Do naszej klasy Auto dodamy dwie metody: jedź i hamuj:
class Auto: def jedz(self):# o self będzie za chwilę print('jadę') def hamuj(self):# o self będzie za chwilę print('hamuję !') tarpan = Auto() # tworzymy obiekt typu Auto i przypisujemy do zmiennej tarpan.jedz() # wywołanie (uruchomienie) metody jedz tarpan.hamuj() # wywołanie (uruchomienie) metody hamuj
No i mamy nasze dwie metody. Zastanawiasz się pewnie dlaczego każda z nich przyjmuje jako parametr zmienną ”self”. Przekazanie ”self” do metody daje jej możliwość używania atrybutów klasy o których zaraz opowiem. Na tą chwilę musisz mi uwierzyć na słowo, że self jest potrzebne jeśli chcemy by metoda działała na obiekcie, a nie na klasie, czyli tak jakby formie z której powstała.
Atrybuty klasy
Atrybuty to nic więcej jak cechy jakie mogą posiadać instancje (obiekty) powstałe na podstawie naszej klasy. Zastanówmy się więc, co może opisać nasz samochód. Wyobraźmy sobie następującą sytuację. Kolega przychodzi do nas i mówi ”kupiłem sobie nowe auto” (na polskie warunki nowe czytaj 5 letnie 🙂 )
O co więc zapytamy naszego kolegę?
- o markę
- o model
Warto pewnie by jeszcze było zapytać czy nie bite i czy licznik nie kręcony, ale poprzestańmy na tych dwóch atrybutach.
Dodawanie atrybutów z wnętrza metody klasy
I tu wracamy do zagadkowego parametru ”self”. To właśnie dzięki niemu będzie możliwe dodanie nowego atrybutu z poziomu wnętrza klasy. Ten przykład powinien wszytko wyjaśnić.
class Auto: def ustaw_marke(self):# ta metoda dodaje atrybut marka i ustawia go na Audi self.marka = 'Audi' auto = Auto() # tworzenie klasy auto print(auto.__dict__) #tutaj nie ma jeszcze atrybutu marka auto.ustaw_marke()# wywołanie (uruchomienie) metody print(auto.__dict__)# teraz atrybut marka już istnieje i jest ustawiony na "Audi"
Dzięki użyciu słowa kluczowego ”self” udało nam się dodać atrybut oraz przypisać mu wartość. Aby dało się przypisywać dowolne wartości do atrybutu marka należy dodać do funkcji parametr:
class Auto: def ustaw_marke(self, marka): self.marka_obiektu = marka auto1 = Auto() print(auto1.__dict__) auto1.ustaw_marke("Audi") print(auto1.__dict__) auto2 = Auto() print(auto2.__dict__) auto2.ustaw_marke("Porsche") print(auto2.__dict__)
Jak widać teraz możemy tworzyć dowolną ilość obiektów na podstawie klasy Auto i dla każdej instancji ustawić dowolną markę. Dokładniej mówiąc atrybutowi marka_obiektu przypisać dowolną wartość.
Konstruktor
Zwykle atrybuty ustawia się w konstruktorze czyli podczas tworzenia obiektu. Konstruktor jest metodą klasy, a dokładniej metodą magiczną. Nie żartuję, serio tak to nazwali. Magia tej metody polega na tym, że uruchamia się ona automatycznie podczas tworzenia obiektu. Właśnie w tej metodzie najlepiej ustawić wartość atrybutów. Python wymusza na nas nazwę metody będącej konstruktorem. Oto ona:
def __init__(self): print('konstruktor działa')
W takim wypadku trzeba troszkę przerobić naszą klasę Auto tak aby w konstruktorze przyjmowała markę i model pojazdu:
class Auto: def __init__(self, marka, model): self.marka_obj = marka #wartość parametru marka zostaje przypisana do atrybutu marka_obj self.model_obj = model #wartość parametru model zostaje przypisana do atrybutu model_obj auto1 = Auto('Audi', 'Q8') # podajemy parametry którymi zajmie się nasz konstruktor print(auto1.__dict__) auto2 = Auto('Porsche', 'Cayenne') print(auto2.__dict__)
Udało się. Teraz wystarczy podać markę i model podczas tworzenia obiektu typu Auto, a konstruktor zrobi za nas resztę roboty.
Co jeśli nie chcemy ustawiać atrybutów konstruktorze ?
Często zdarza się tak, że w momencie tworzenia obiektu nie znamy jeszcze wartości atrybutów i chcemy dodać je później. Dobrą praktyką jet mimo wszytko stworznie tych atrybutów w konstruktorze i przypisanie im wartości ”None”. Potem w dalszej części programu przypiszemy im właściwe wartości:
class Auto: def __init__(self): self.marka_obj = None #każdy nowo powstały obiekt będzie miał dwa atrybuty marka_obj i model_obj ustawione na wartość "None" self.model_obj = None auto1 = Auto() print(auto1.__dict__) auto1.marka_obj = 'Audi' auto1.model_obj = 'Q8' auto2 = Auto() auto2.marka_obj = 'Porsche' auto2.model_obj = 'Cayenne' print(auto2.__dict__)
Takie podejście powoduje, że wszystkie obiekty zbudowane w ten sposób będą miały ten sam wachlarz atrybutów.
Co powinieneś umieć po przeczytaniu tego wpisu
- tworzyć proste klasy
- rozumieć różnicę pomiędzy klasą (formą), a obiektem / instancją (powstała na podstawie formy)
- stworzyć własny konstruktor
- umieć przekazywać parametry do konstruktora
To na tyle w temacie podstaw obiektowości. W kolejnym wpisie porozmawiamy więcej o konstruktorach oraz zbudujemy bardziej skomplikowany model naszego samochodu.