g1 = Gracz()
Programowanie obiektowe · rozdział 8 · klasa III

Klasy i obiekty

Wchodzimy w programowanie obiektowe.

Dziś: 2 × (teoria + Wasz kod).
Teoria 1

Świat składa się z obiektów

Każdy obiekt wokół nas ma cechy (jaki jest) i zachowania (co potrafi). Programowanie obiektowe przenosi ten sposób myślenia do kodu.

Gracz

cechy: nick, punkty · zachowania: rzuca, zbiera punkty

Konto bankowe

cechy: saldo, właściciel · zachowania: wpłać, wypłać

Samochód

cechy: marka, przebieg · zachowania: jedź, hamuj

Kostka do gry

cechy: liczba oczek · zachowania: rzuć

W kodzie: cechy nazywamy polami (właściwościami), a zachowania — metodami.
Teoria 1

Klasa i obiekt

KLASA — Gracz

nick, punkty · przywitaj()

OBIEKTY (instancje)

g1 → nick "Ola", punkty 40 · g2 → nick "Bartek", punkty 15 · g3 → nick "Iga", punkty 0

Jedna klasa — wiele obiektów. Każdy obiekt ma WŁASNE wartości pól, ale wspólny kształt i te same metody. Jak forma do ciastek i ciastka: przepis jeden, ciastek dowolnie dużo.
Słowniczek rozdziału 8: obiekt (instancja), klasa, hermetyzacja, agregacja, dziedziczenie, polimorfizm — dwa pierwsze dziś, hermetyzacja za godzinę, reszta na kolejnych zajęciach.
Teoria 1

Pierwsza klasa w Pythonie

class Gracz:                     # definicja klasy (szablonu)
    nick = ""                    # pole = cecha
    punkty = 0                   # drugie pole, start od zera

    def przywitaj(self):         # metoda = zachowanie
        print("Jestem", self.nick)   # self.nick = pole TEGO obiektu
Dwa klasyczne błędy dnia: złe wcięcia (w Pythonie to one wyznaczają, co należy do klasy i metody) oraz zapomniany self w nawiasie metody.
Sama definicja klasy jeszcze NIC nie robi — to tylko szablon. Życie zaczyna się od obiektów.
Teoria 1

Obiekty w akcji — kropka

g1 = Gracz()            # tworzymy obiekt (instancję) klasy
g1.nick = "Ola"         # kropka = dostęp do pola
g1.punkty = 40
g1.przywitaj()          # wywołanie metody — bez self!

g2 = Gracz()            # drugi obiekt — NIEZALEŻNY
g2.nick = "Bartek"      # własne dane g2
g2.przywitaj()          # ta sama metoda, inny obiekt
wynik: Jestem Ola  /  Jestem Bartek
Pytanie kontrolne: co wypisze g1.przywitaj() wywołane PO ustawieniu g2? Dlaczego nick się nie pomieszał?
Praktyka 1 · ~40 min

Praktyka 1: Twoja pierwsza klasa

1Twoja pierwsza klasa

  • klasa Gracz: pola nick i punkty
  • metoda dodaj_punkty(ile) — zwiększa punkty
  • metoda wypisz() — nick i punkty w jednej linii
  • w main: 2 graczy, każdemu dodaj punkty 2 razy, wypisz obu

✓Ma być na ekranie

  • Ola: 65 pkt
  • Bartek: 30 pkt
  • zalicza: działa i wynik jak obok
  • nazwy pól i metod — jak w zadaniu

★Dla szybkich

  • metoda czy_wygral() zwracająca True od 100 punktów + trzeci gracz, który wygrywa
  • a potem: klasa Kostka z metodą rzuc()
Teoria 2

Problem: każdy może wszystko

g1.punkty = -999      # Python nawet nie mrugnie...
g1.punkty = 1000000   # ...oszustwo w 1 linii
g1.nick = ""          # gracz bez imienia?
Pola są public, więc KAŻDY fragment programu może wpisać w nie cokolwiek. W małym programie to „tylko” bug. W banku — katastrofa: saldo konta nie może być polem, które każdy sobie ustawia.
Hermetyzacja (enkapsulacja): pola chowamy (private), a świat rozmawia z obiektem TYLKO przez metody — jak z bankomatem: nie grzebiesz w sejfie, naciskasz przyciski, a automat pilnuje reguł.
Teoria 2

Hermetyzacja w kodzie

class Gracz:
    nick = ""
    __punkty = 0                  # dwa podkreślenia = pole ukryte

    def dodaj_punkty(self, ile):
        if ile > 0:               # metoda PILNUJE reguł
            self.__punkty += ile

    def pobierz_punkty(self):     # tzw. getter
        return self.__punkty      # odczyt: tak, zapis: nie

print(g1.__punkty)   -> AttributeError   # obiekt broni dostępu
Szczerze: w Pythonie to umowa i utrudnienie, nie pancerna ściana. Twardy private, którego pilnuje kompilator, poznacie w C++ (rozdział 9).
Praktyka 2 · ~40 min

Praktyka 2: uszczelnij swoją klasę

2Uszczelnij swoją klasę

  • zmień pole na __punkty (ukryte)
  • dodaj_punkty: odrzuca ile <= 0 i wypisuje komunikat
  • dodaj pobierz_punkty() i używaj go w wypisz()
  • sprawdź: print(g1.__punkty) MA rzucić AttributeError (potem tę linię usuń)

★Dla szybkich — Kostka

  • klasa Kostka: private pole wynik
  • metoda rzuc(): losuje 1–6 (random.randint(1, 6))
  • metoda pobierz_wynik()
  • na końcu: gracz rzuca kostką 5 razy i zbiera oczka przez dodaj_punkty
Podpowiedź do gwiazdki: dwa obiekty różnych klas współpracują — Gracz zbiera to, co wylosowała Kostka. Tak w praktyce wygląda agregacja ze słowniczka.
Przenośność pojęć

Te same idee, inne języki

Python

class Gracz:
    nick = ""
    def przywitaj(self):
        print(...)

C++ — rozdział 9

class Gracz {
public:
    string nick;
    void przywitaj()
    { ... }
};

Kotlin — znacie z aplikacji mobilnych

class Gracz(var nick: String) {
    fun przywitaj()
    { ... }
}
Programowanie obiektowe to sposób myślenia, nie składnia. Klasa, obiekt, pola, metody, hermetyzacja — te pojęcia przenosicie między językami za darmo. Na egzaminie INF.04 część konsolową piszecie w dowolnym z: C++, C#, Java, Python.
Podsumowanie

Zapamiętaj

Klasa to szablon, obiekt to instancja

jedna klasa, wiele obiektów z własnymi danymi.

Pola = cechy, metody = zachowania

do składników obiektu sięgamy kropką, a self to „ten obiekt”.

Hermetyzacja

pola ukrywamy (__pole), świat rozmawia przez metody, które pilnują reguł.

W Pythonie o strukturze decydują wcięcia — pilnuj ich jak reguł gry.
Następne zajęcia: konstruktory (rozdział 9) — obiekty, które rodzą się gotowe. Tam też wchodzi C++.