Wprowadzenie do obiektów

Dotychczas konstruowaliście swoje programy jako jeden ciąg instrukcji, które wykonywane były w tej takiej kolejności, jak zostały napisane. Czasem deklarowaliście też funkcje. Python oferuje również inny sposób tworzenia programów : programowanie obiektowe. Chcąc nie chcąc - musicie korzystać z obiektów. W Pythonie spotykamy je na każdym kroku. Obiektem jest lista, słownik, wyrażenie regularne, a także otwary plik. Ten wykład pokaże Wam, jak deklarować własne obiekty i pomoże zrozumieć jak one działają.

Czym jest obiekt?

Obiekt to struktura, która zawiera w sobie dane (zmienne) oraz funkcje

Obiekt może zawierać też struktury danych (listy, słowniki, tuple) a także inne obiekty. Nowy styl programowania (czyli obiektowy) wiąże się też z nowym nazewnictwem: zmienne obiektu nazywamy jego polami; jego funkcje zaś - metodami. Do tych składowych obiektu odwołujemy się pisząc nazwę obiektu, kropkę, a po niej - nazwę pola lub metody. Ze sposobem tym spotkaliście się już, np. korzystając z list. Spis pól i metod, dostępnych w danym obiekcie, możemy znaleźć w jego dokumentacji.

Klasa - deklarowanie obiektu

Zanim zagłębimy się w klasy, powróćmy jeszcze na moment do tego rozdziału) wykładu o funkcjach. Znajdujący się tam przykład definiuje funkcję funkcja_kwadratowa(), którą można potem użyć i to więcej niż raz. Jeżeli jednak zakomentujesz 6 linię tego kodu, program nic nie zrobi. Zadeklarowanie funkcji to co innego niż jej użycie.

Oto przykład, w którym deklarujemy klasę obiektów. Klasa nazywa się Vec3 i opisuje obiekty będące wektorami w przestrzeni 3D. W liniach 3-22 deklarujemy klasę, a potem w 24-25 tworzymy obiekty tej klasy. Podobnie jak z funkcjami - klasę deklarujemy raz, a możemy stworzyć wiele takich obiektów. Na koniec, w linii 26 wywołujemy metodę length(), w linii 27 - metodę add() a w linii 29 - odczytujemy wartości pól obiektu v1.

Powyższy przykład, choć bardzo krótki i zapewnie niekompletny, pokazuje kilka ważnych kwestii związanych z tworzeniem obiektów:

  • każdy obiekt działa na swoich danych; dla przykładu jest tylko jedna metoda length(), a wyniki jej wywołania w linii różne 15 są różne

  • każda klasa może mieć specjalną metodę __init__(), zwaną konstruktorem klasy; konstruktor tworzy pola w klasie i ewentualnie nadaje im wartości

  • jedynym sposobem na stworzenie pól w obiekcie, jest zainicjowanie ich w konstruktorze; klasa Vec3 ma zatem trzy pola: x, y i z

  • każda metoda klasy musi mieć co najmniej jeden argument, który jest referencją do obiektu ‘samego siebie’; przy wywoływaniu metody w klasie nie wykorzystujemy go; dla przykładu - metoda add() ma dwa argumenty: self i another_vector, ale wywołując ją, podajemy tylko jeden argument - wektor będący drugim składnikiem dodawania

  • zmienna self jest potrzeba metodzie, aby mogła dostać się do swoich danych, czyli pól tego obiektu, na którym pracuje

  • obiekty Tworzymy, pisząc nazwę klasy tak, jakby była funkcją - dodajemy argumenty w nawiasach

Metody specjalne

W klasach w języku Python można zdefiniować pewne specjalne metody (ang. magic methods), które mają szczególne znaczenie. Jedną z nich już poznaliście - metoda __init__(), która służy do stworzenia obiektu. Kolejna to metoda __str__(), która musi zwracać napis (zmienną typu string). Metoda ta będzie wykorzystana za każdym razem, kiedy użytkownik wywoła funkcję print() aby wydrukować obiekt. Poniżej znajduje się zmodyfikowany przykład klasy Vec3, do której dodałem metodę __str__(). Teraz już można napisać print(v1) a na ekranie pokażą się współrzędne:

Zauważ, że zarówno metoda __str__() nie jest jawnie wywoływana, podobnie jak nie wywoływaliśmy __init__().

Praca domowa

Dopisz do powyższej klasy metodę sub(another_vector), która będzie odejmowała another_vector od danego obiektu. Jej działanie będzie zatem bardzo podobne do add(another_vector), która dodaje. Następnie w programie zadeklaruj 3 wektory: \(v_1 = (1,2,0)\), \(v_2 = (0,2,3)\) oraz \(v_3 = (2,2,1)\) i wydrukuj wartość wyrażenia:

\[\frac{(v_1 - v_2) * (v_1 + v_3)}{|v_1|}\]

gdzie \(|v_1|\) oznacza długość wektora \(v_1\). a \(*\) - iloczyn skalarny (dot product). Poprawny wynik: 0.0.

Podpowiedź: najwygodniej Ci będzie, kiedy stworzysz dwa obiekty zawierające wektor \(v_1\), np v1a i v1b.