Programowanie obiektowe (Object-Oriented Programming, OOP) to paradygmat programowania, który organizuje kod wokół obiektów i klas. Zamiast myśleć o programie jako o sekwencji instrukcji, myślisz o nim jako o zbiorze obiektów, które ze sobą współpracują.
O tym po co nam programie obiektowe możesz przeczytać niżej w materiale.
Kluczowe koncepcje:
- Klasa – szablon/przepis na obiekt
- Obiekt – konkretna instancja klasy
- Atrybuty – dane/właściwości obiektu
- Metody – funkcje należące do obiektu
Analogia:
- Klasa = Przepis na ciasto
- Obiekt = Konkretne ciasto upieczone według przepisu
- Atrybuty = Składniki ciasta (mąka, jajka, cukier)
- Metody = Akcje (pieczenie, dekorowanie)
Klasa i obiekt
Czym jest klasa?
Klasa to szablon/wzorzec definiujący:
- Jakie dane będzie przechowywać obiekt (atrybuty)
- Jakie operacje może wykonać (metody)
Analogia: Klasa to formularz, obiekt to wypełniony formularz.
Tworzenie pierwszej klasy
class Kot:
pass # Pusta klasa
# Tworzenie obiektu (instancji)
mruczek = Kot()
print(type(mruczek)) # <class '__main__.Kot'>
Klasa z atrybutami
class Test:
# Atrybuty klasy (wspólne dla wszystkich instancji)
wlasnosc = "Jestem własnością klasy Test"
lista = ["kot", "pies", "królik"]
slownik = {"k1": "v1", "k2": "v2"}
# Tworzenie obiektu
obiekt = Test()
# Dostęp do atrybutów
print(obiekt.wlasnosc) # Jestem własnością klasy Test
print(obiekt.lista[0]) # kot
print(obiekt.slownik["k1"]) # v1
Prywatne atrybuty
W Pythonie nie ma prawdziwych modyfikatorów dostępu. Używamy konwencji:
nazwa– publiczne_nazwa– chronione (konwencja, nie używaj poza klasą)__nazwa– prywatne (name mangling)
class Test:
publiczne = "Dostępne wszędzie"
_chronione = "Konwencja - nie używaj"
__prywatne = "Trudno dostępne"
obiekt = Test()
print(obiekt.publiczne) # OK
# print(obiekt.__prywatne) # AttributeError
# Można dostać się, ale to złamanie konwencji
print(obiekt._Test__prywatne) # Zadziała, ale nie rób tego!
Metody
Metoda to funkcja zdefiniowana wewnątrz klasy.
Podstawowa metoda
class Pies:
def szczekaj(self):
# self - odwołanie do instancji obiektu
print("Hau hau!")
azor = Pies()
azor.szczekaj() # Hau hau!
Ważne: Pierwszy parametr metody to zawsze self (konwencja).
Metody z parametrami
class Kalkulator:
def dodaj(self, a, b):
return a + b
def odejmij(self, a, b):
return a - b
kalk = Kalkulator()
print(kalk.dodaj(5, 3)) # 8
print(kalk.odejmij(10, 4)) # 6
Metody korzystające z atrybutów
class Osoba:
imie = "Jan"
def przywitaj(self):
# self.imie - dostęp do atrybutu
print(f"Cześć, jestem {self.imie}!")
osoba = Osoba()
osoba.przywitaj() # Cześć, jestem Jan!
Konstruktor – init
Konstruktor to specjalna metoda wywoływana automatycznie przy tworzeniu obiektu.
Podstawowy konstruktor
class Kot:
def __init__(self, imie, wiek):
"""Konstruktor - inicjalizuje obiekt"""
self.imie = imie # Atrybut instancji
self.wiek = wiek
def info(self):
print(f"{self.imie} ma {self.wiek} lat")
# Podczas tworzenia obiekt wywołuje __init__
mruczek = Kot("Mruczek", 3)
mruczek.info() # Mruczek ma 3 lata
puszek = Kot("Puszek", 5)
puszek.info() # Puszek ma 5 lat
Konstruktor z wartościami domyślnymi
class Gracz:
def __init__(self, nick, hp=100, lvl=1):
self.nick = nick
self.hp = hp
self.lvl = lvl
def __str__(self):
return f"{self.nick} (HP: {self.hp}, Lvl: {self.lvl})"
# Z domyślnymi wartościami
gracz1 = Gracz("Warrior")
print(gracz1) # Warrior (HP: 100, Lvl: 1)
# Nadpisanie domyślnych
gracz2 = Gracz("Mage", hp=80, lvl=5)
print(gracz2) # Mage (HP: 80, Lvl: 5)
Metody magiczne (dunder methods)
Metody magiczne to specjalne metody otoczone podwójnymi podkreślnikami: __nazwa__.
str – reprezentacja tekstowa
class Ksiazka:
def __init__(self, tytul, autor):
self.tytul = tytul
self.autor = autor
def __str__(self):
"""Wywoływane przez print() i str()"""
return f'"{self.tytul}" - {self.autor}'
ksiazka = Ksiazka("Wiedźmin", "Andrzej Sapkowski")
print(ksiazka) # "Wiedźmin" - Andrzej Sapkowski
repr – reprezentacja techniczna
class Ksiazka:
def __init__(self, tytul, autor, rok):
self.tytul = tytul
self.autor = autor
self.rok = rok
def __str__(self):
# Dla użytkownika
return f'"{self.tytul}" - {self.autor} ({self.rok})'
def __repr__(self):
# Dla programisty/debugowania
return f'Ksiazka(tytul="{self.tytul}", autor="{self.autor}", rok={self.rok})'
ksiazka = Ksiazka("Wiedźmin", "Sapkowski", 1990)
print(str(ksiazka)) # "Wiedźmin" - Sapkowski (1990)
print(repr(ksiazka)) # Ksiazka(tytul="Wiedźmin", autor="Sapkowski", rok=1990)
len – długość obiektu
class Playlista:
def __init__(self, nazwa):
self.nazwa = nazwa
self.utwory = []
def dodaj(self, utwor):
self.utwory.append(utwor)
def __len__(self):
"""Wywoływane przez len()"""
return len(self.utwory)
pl = Playlista("Rock")
pl.dodaj("Song 1")
pl.dodaj("Song 2")
print(len(pl)) # 2
Operatory porównania
class Osoba:
def __init__(self, imie, wiek):
self.imie = imie
self.wiek = wiek
def __eq__(self, other):
"""Operator =="""
return self.wiek == other.wiek
def __lt__(self, other):
"""Operator <"""
return self.wiek < other.wiek
def __gt__(self, other):
"""Operator >"""
return self.wiek > other.wiek
anna = Osoba("Anna", 25)
jan = Osoba("Jan", 30)
print(anna < jan) # True
print(jan > anna) # True
print(anna == jan) # False
getitem i setitem – indeksowanie
class Lista:
def __init__(self):
self.dane = []
def __getitem__(self, index):
"""Wywoływane przez lista[index]"""
return self.dane[index]
def __setitem__(self, index, value):
"""Wywoływane przez lista[index] = value"""
self.dane[index] = value
def dodaj(self, element):
self.dane.append(element)
lista = Lista()
lista.dodaj("Python")
lista.dodaj("Java")
lista.dodaj("C++")
print(lista[0]) # Python - wywołuje __getitem__
lista[1] = "JavaScript" # Wywołuje __setitem__
print(lista[1]) # JavaScript
Operatory matematyczne
class Wektor:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
"""Operator +"""
return Wektor(self.x + other.x, self.y + other.y)
def __mul__(self, skalar):
"""Operator *"""
return Wektor(self.x * skalar, self.y * skalar)
def __str__(self):
return f"({self.x}, {self.y})"
v1 = Wektor(1, 2)
v2 = Wektor(3, 4)
v3 = v1 + v2 # Wywołuje __add__
print(v3) # (4, 6)
v4 = v1 * 3 # Wywołuje __mul__
print(v4) # (3, 6)
call – obiekt jako funkcja
class Mnoznik:
def __init__(self, mnoznik):
self.mnoznik = mnoznik
def __call__(self, x):
"""Pozwala wywołać obiekt jak funkcję"""
return x * self.mnoznik
razy_2 = Mnoznik(2)
razy_10 = Mnoznik(10)
print(razy_2(5)) # 10
print(razy_10(5)) # 50
Tabela metod magicznych
| Metoda | Wywołanie | Opis |
|---|---|---|
__init__ | obj = Klasa() | Konstruktor |
__str__ | print(obj), str(obj) | Reprezentacja tekstowa |
__repr__ | repr(obj) | Reprezentacja techniczna |
__len__ | len(obj) | Długość |
__eq__ | obj1 == obj2 | Równość |
__lt__ | obj1 < obj2 | Mniejsze niż |
__gt__ | obj1 > obj2 | Większe niż |
__add__ | obj1 + obj2 | Dodawanie |
__sub__ | obj1 - obj2 | Odejmowanie |
__mul__ | obj1 * obj2 | Mnożenie |
__getitem__ | obj[key] | Dostęp przez indeks |
__setitem__ | obj[key] = val | Ustawianie przez indeks |
__call__ | obj() | Wywołanie jak funkcja |
__del__ | Usuwanie obiektu | Destruktor |
Modyfikatory dostępu
Python nie ma prawdziwych modyfikatorów dostępu jak Java czy C++. Używa konwencji:
Publiczne (brak podkreślników)
class Osoba:
def __init__(self, imie):
self.imie = imie # Publiczne
osoba = Osoba("Jan")
print(osoba.imie) # OK
osoba.imie = "Anna" # OK
Chronione (jeden podkreślnik)
class Osoba:
def __init__(self, imie):
self._imie = imie # Chronione (konwencja)
def _metoda_wewnetrzna(self):
print("Używana tylko wewnątrz")
osoba = Osoba("Jan")
print(osoba._imie) # Technicznie działa, ale nie powinieneś
Konwencja: _nazwa sugeruje „nie używaj tego poza klasą”.
Prywatne (dwa podkreślniki)
class Osoba:
def __init__(self, imie):
self.__imie = imie # Prywatne
def __metoda_prywatna(self):
print("Prywatna metoda")
def pokaz_imie(self):
print(self.__imie) # OK wewnątrz klasy
osoba = Osoba("Jan")
# print(osoba.__imie) # AttributeError
# Python zmienia nazwę (name mangling)
print(osoba._Osoba__imie) # Działa, ale nie rób tego!
Name mangling: Python zmienia __nazwa na _NazwaKlasy__nazwa.
Dziedziczenie
Dziedziczenie pozwala tworzyć nowe klasy bazując na istniejących.
Podstawowe dziedziczenie
class Zwierze:
def __init__(self, imie):
self.imie = imie
def dzwiek(self):
return "Jakiś dźwięk"
class Pies(Zwierze): # Pies dziedziczy po Zwierze
def dzwiek(self):
return "Hau hau!"
class Kot(Zwierze):
def dzwiek(self):
return "Miau!"
pies = Pies("Azor")
kot = Kot("Mruczek")
print(f"{pies.imie}: {pies.dzwiek()}") # Azor: Hau hau!
print(f"{kot.imie}: {kot.dzwiek()}") # Mruczek: Miau!
super() – wywołanie metod rodzica
class Pojazd:
def __init__(self, marka):
self.marka = marka
print(f"Utworzono pojazd: {marka}")
def info(self):
return f"Pojazd marki {self.marka}"
class Samochod(Pojazd):
def __init__(self, marka, model):
super().__init__(marka) # Wywołanie konstruktora rodzica
self.model = model
def info(self):
# Wywołanie metody rodzica
podstawowa_info = super().info()
return f"{podstawowa_info}, model {self.model}"
auto = Samochod("Toyota", "Corolla")
# Utworzono pojazd: Toyota
print(auto.info())
# Pojazd marki Toyota, model Corolla
isinstance() – sprawdzanie typu
class Zwierze:
pass
class Pies(Zwierze):
pass
class Kot(Zwierze):
pass
pies = Pies()
kot = Kot()
print(isinstance(pies, Pies)) # True
print(isinstance(pies, Zwierze)) # True (dziedziczy!)
print(isinstance(pies, Kot)) # False
print(isinstance(pies, object)) # True (wszystko dziedziczy po object)
Wielodziedziczenie
Python obsługuje dziedziczenie z wielu klas (ostrożnie!).
class Latanie:
def lataj(self):
print("Lecę!")
class Plywanie:
def plywaj(self):
print("Pływam!")
class Kaczka(Latanie, Plywanie):
def kwacz(self):
print("Kwa kwa!")
kaczka = Kaczka()
kaczka.lataj() # Lecę!
kaczka.plywaj() # Pływam!
kaczka.kwacz() # Kwa kwa!
Kompozycja vs Dziedziczenie
W OOP (programowaniu obiektowym) dziedziczenie i kompozycja to dwa sposoby budowania relacji między klasami.
Zadaj sobie pytanie:
Czy mogę powiedzieć: X jest Y
Przykłady:
- Pies jest zwierzęciem – tak
- Student jest człowiekiem – tak
- Samochód jest pojazdem – tak
→ dziedziczenie
Czy mogę powiedzieć: X ma Y
Przykłady:
- Samochód ma silnik – tak
- Dom ma okna – tak
- Komputer ma procesor – tak
→ kompozycja
Kiedy używać dziedziczenia?
Relacja „JEST” (is-a):
- Pies JEST Zwierzęciem
- Samochód JEST Pojazdem
class Zwierze:
pass
class Pies(Zwierze): # Pies JEST Zwierzęciem
pass
Kiedy używać kompozycji?
Relacja „MA” (has-a):
- Samochód MA Silnik
- Osoba MA Adres
class Silnik:
def __init__(self, moc):
self.moc = moc
def uruchom(self):
print(f"Silnik {self.moc}KM uruchomiony")
class Samochod:
def __init__(self, marka, moc_silnika):
self.marka = marka
self.silnik = Silnik(moc_silnika) # MA silnik
def uruchom(self):
print(f"Uruchamiam {self.marka}")
self.silnik.uruchom()
auto = Samochod("Toyota", 150)
auto.uruchom()
# Uruchamiam Toyota
# Silnik 150KM uruchomiony
Problem z dziedziczeniem
# ŹLE - nadmierne dziedziczenie
class Ptak:
def lataj(self):
print("Lecę")
class Pingwin(Ptak):
# Problem! Pingwin nie lata, ale dziedziczy lataj()
pass
pingwin = Pingwin()
pingwin.lataj() # Pingwin lata? To błąd logiczny!
Rozwiązanie – kompozycja
# DOBRZE - kompozycja
class Latanie:
def lataj(self):
print("Lecę w powietrzu")
class Plywanie:
def plywaj(self):
print("Pływam w wodzie")
class Orzel:
def __init__(self):
self.latanie = Latanie() # MA zdolność latania
def wykonaj_lot(self):
self.latanie.lataj()
class Pingwin:
def __init__(self):
self.plywanie = Plywanie() # MA zdolność pływania
def wykonaj_plywanie(self):
self.plywanie.plywaj()
orzel = Orzel()
orzel.wykonaj_lot() # Lecę w powietrzu
pingwin = Pingwin()
pingwin.wykonaj_plywanie() # Pływam w wodzie
Złota zasada: „Prefer composition over inheritance” (Preferuj kompozycję nad dziedziczeniem)
Klasy abstrakcyjne
Klasa abstrakcyjna to klasa, która:
- Nie może być instancjonowana (nie można z niej tworzyć obiektów)
- Służy jako szablon dla klas potomnych
- Może zawierać metody abstrakcyjne (bez implementacji)
from abc import ABC, abstractmethod
class Zwierze(ABC):
def __init__(self, imie):
self.imie = imie
@abstractmethod
def dzwiek(self):
"""Metoda abstrakcyjna - musi być zaimplementowana"""
pass
def przedstaw_sie(self):
print(f"Jestem {self.imie} i mówię {self.dzwiek()}")
# zwierze = Zwierze("Test") # TypeError! Nie można utworzyć
class Pies(Zwierze):
def dzwiek(self):
return "Hau hau!"
class Kot(Zwierze):
def dzwiek(self):
return "Miau!"
pies = Pies("Azor")
pies.przedstaw_sie() # Jestem Azor i mówię Hau hau!
Dekorator @property
@property pozwala traktować metodę jak atrybut.
class Prostokat:
def __init__(self, szerokosc, wysokosc):
self.szerokosc = szerokosc
self.wysokosc = wysokosc
@property
def pole(self):
"""Metoda jako właściwość"""
return self.szerokosc * self.wysokosc
@property
def obwod(self):
return 2 * (self.szerokosc + self.wysokosc)
prostokat = Prostokat(5, 10)
# Wywołanie bez nawiasów!
print(prostokat.pole) # 50
print(prostokat.obwod) # 30
Getter i setter
class Osoba:
def __init__(self, imie, wiek):
self._imie = imie
self._wiek = wiek
@property
def wiek(self):
"""Getter"""
return self._wiek
@wiek.setter
def wiek(self, wartosc):
"""Setter z walidacją"""
if wartosc < 0:
raise ValueError("Wiek nie może być ujemny!")
self._wiek = wartosc
osoba = Osoba("Jan", 25)
print(osoba.wiek) # 25 - wywołuje getter
osoba.wiek = 30 # Wywołuje setter
print(osoba.wiek) # 30
# osoba.wiek = -5 # ValueError!
Paradygmaty OOP
1. Abstrakcja
Ukrywanie skomplikowanych szczegółów, pokazywanie tylko tego co potrzebne.
class Telefon:
def __init__(self, model):
self.model = model
self.__system = "Android" # Ukryte
def dzwon(self, numer):
"""Prosty interfejs - nie musisz wiedzieć jak działa"""
self.__polacz_z_sieciami()
self.__wyslij_sygnal(numer)
print(f"Dzwonię na {numer}")
def __polacz_z_sieciami(self):
"""Ukryte szczegóły"""
pass
def __wyslij_sygnal(self, numer):
"""Ukryte szczegóły"""
pass
telefon = Telefon("iPhone")
telefon.dzwon("123456789") # Prosta, ukrywa złożoność
2. Enkapsulacja
Ukrywanie danych i kontrolowanie dostępu do nich.
class KontoBankowe:
def __init__(self, saldo):
self.__saldo = saldo # Prywatne - enkapsulacja
def wplata(self, kwota):
if kwota > 0:
self.__saldo += kwota
return True
return False
def wyplata(self, kwota):
if 0 < kwota <= self.__saldo:
self.__saldo -= kwota
return True
return False
@property
def saldo(self):
"""Kontrolowany dostęp tylko do odczytu"""
return self.__saldo
konto = KontoBankowe(1000)
konto.wplata(500)
print(konto.saldo) # 1500
# konto.__saldo = 999999 # Nie działa - chronione!
3. Dziedziczenie
Tworzenie hierarchii klas, współdzielenie kodu.
class Pojazd:
def __init__(self, marka):
self.marka = marka
def info(self):
return f"Pojazd {self.marka}"
class Samochod(Pojazd):
def __init__(self, marka, model):
super().__init__(marka)
self.model = model
def info(self):
return f"Samochód {self.marka} {self.model}"
class Motocykl(Pojazd):
def info(self):
return f"Motocykl {self.marka}"
4. Polimorfizm
Różne obiekty reagują na tę samą metodę w różny sposób.
class Pies:
def dzwiek(self):
return "Hau hau!"
class Kot:
def dzwiek(self):
return "Miau!"
class Krowa:
def dzwiek(self):
return "Muuu!"
# Polimorfizm - ta sama metoda, różne zachowania
zwierzeta = [Pies(), Kot(), Krowa()]
for zwierze in zwierzeta:
print(zwierze.dzwiek())
# Hau hau!
# Miau!
# Muuu!
Przykłady praktyczne
Zadanie 1: Klasa Trójkąt
import random
class Trojkat:
def __init__(self):
self.wysokosc = random.randint(1, 20)
self.podstawa = random.randint(1, 20)
def oblicz_pole(self):
return (self.podstawa * self.wysokosc) / 2
def __str__(self):
return f"Trójkąt(podstawa={self.podstawa}, wysokość={self.wysokosc})"
trojkat1 = Trojkat()
trojkat2 = Trojkat()
pole1 = trojkat1.oblicz_pole()
pole2 = trojkat2.oblicz_pole()
print(f"{trojkat1} - Pole: {pole1} cm²")
print(f"{trojkat2} - Pole: {pole2} cm²")
if pole1 > pole2:
print(f"Trójkąt 1 jest większy")
else:
print(f"Trójkąt 2 jest większy")
Zadanie 2: Okrąg i Koło
from math import pi
class Okrag:
def __init__(self, promien):
self.promien = promien
def obwod(self):
return 2 * pi * self.promien
class Kolo(Okrag):
def pole(self):
return pi * self.promien ** 2
kolo = Kolo(10)
print(f"Obwód: {kolo.obwod():.2f}")
print(f"Pole: {kolo.pole():.2f}")
Zadanie 3: Gracz RPG
import random
class Gracz:
def __init__(self, nick):
self.nick = nick
self.poziom = 1
self.hp = 100
self.exp = 0
def atak(self, cel):
obrazenia = random.randint(10, 25)
print(f"{self.nick} atakuje {cel.nick}!")
cel.otrzymaj_obrazenia(obrazenia)
def otrzymaj_obrazenia(self, ilosc):
self.hp -= ilosc
if self.hp < 0:
self.hp = 0
print(f"{self.nick} ma {self.hp} HP")
def zdobadz_exp(self, ilosc):
self.exp += ilosc
if self.exp >= 100:
self.poziom += 1
self.exp = 0
print(f"{self.nick} awansował na poziom {self.poziom}!")
def __str__(self):
return f"{self.nick} | Poziom: {self.poziom} | HP: {self.hp} | EXP: {self.exp}"
gracz1 = Gracz("Warrior")
gracz2 = Gracz("Mage")
gracz1.atak(gracz2)
Po co programowanie obiektowe?
1. Organizacja i modularność
Kod podzielony na logiczne części (klasy) jest bardziej przejrzysty.
# Bez OOP - wszystko w jednym miejscu
gracz_imie = "Jan"
gracz_hp = 100
gracz_lvl = 1
przeciwnik_imie = "Goblin"
przeciwnik_hp = 50
przeciwnik_lvl = 1
# Z OOP - każdy obiekt osobno
class Postac:
def __init__(self, imie, hp, lvl):
self.imie = imie
self.hp = hp
self.lvl = lvl
gracz = Postac("Jan", 100, 1)
przeciwnik = Postac("Goblin", 50, 1)
2. Reużywalność kodu
Możesz wykorzystywać klasy wielokrotnie.
class Pojazd:
def __init__(self, marka):
self.marka = marka
def jedz(self):
print(f"{self.marka} jedzie")
# Możesz utworzyć wiele obiektów tej samej klasy
samochod1 = Pojazd("Toyota")
samochod2 = Pojazd("BMW")
samochod3 = Pojazd("Audi")
3. Łatwiejsze rozwijanie aplikacji
Zmiany w jednej klasie nie wpływają na inne.
# Dodanie nowej funkcjonalności tylko w jednym miejscu
class Samochod:
def __init__(self, marka):
self.marka = marka
self.predkosc = 0
def przyspiesz(self, ile):
self.predkosc += ile
print(f"Prędkość: {self.predkosc} km/h")
4. Enkapsulacja – ukrywanie szczegółów
Dane są chronione, dostęp tylko przez metody.
class KontoBankowe:
def __init__(self, saldo):
self.__saldo = saldo # Prywatne
def wplac(self, kwota):
if kwota > 0:
self.__saldo += kwota
def pobierz_saldo(self):
return self.__saldo
# Nie można bezpośrednio zmieniać salda
konto = KontoBankowe(1000)
konto.wplac(500) # OK
# konto.__saldo = 999999 # Nie zadziała!
5. Naturalne odwzorowanie rzeczywistości
OOP naśladuje sposób myślenia o świecie.
class Kot:
def __init__(self, imie, kolor):
self.imie = imie
self.kolor = kolor
def miaucz(self):
print(f"{self.imie}: Miauuu!")
mruczek = Kot("Mruczek", "rudy")
mruczek.miaucz() # Mruczek: Miauuu!
Podsumowanie
Podstawy OOP:
- Klasa – szablon obiektu
- Obiekt – instancja klasy
- Atrybuty – dane obiektu
- Metody – funkcje obiektu
- self – odniesienie do instancji
Metody specjalne:
__init__– konstruktor__str__– reprezentacja tekstowa__len__– długość__eq__,__lt__,__gt__– porównania__add__,__mul__– operatory matematyczne__getitem__,__setitem__– indeksowanie
Zaawansowane:
- Dziedziczenie – tworzenie hierarchii klas
- super() – wywołanie metod rodzica
- Kompozycja – obiekty zawierają inne obiekty
- Klasy abstrakcyjne – szablony dla klas potomnych
- @property – metody jako atrybuty
Paradygmaty OOP:
- Abstrakcja – ukrywanie szczegółów
- Enkapsulacja – ochrona danych
- Dziedziczenie – współdzielenie kodu
- Polimorfizm – jedna metoda, różne zachowania
Dobre praktyki:
- Jedna klasa = jedna odpowiedzialność
- Preferuj kompozycję nad dziedziczeniem
- Używaj
@propertydla computed properties - Dokumentuj klasy docstringami
- Nazwy klas PascalCase
Ćwiczenia – Programowanie obiektowe
Podstawy klas i obiektów
- Stwórz klasę
Punktz atrybutami x i y. Dodaj metodę wyświetlającą współrzędne. - Stwórz klasę
Prostokatz atrybutami szerokość i wysokość. Dodaj metody obliczające pole i obwód. - Stwórz klasę
Koloz atrybutem promień. Dodaj metody obliczające pole i obwód. - Stwórz klasę
Studentz atrybutami: imię, nazwisko, numer_indeksu. Dodaj metodę wyświetlającą pełne dane. - Stwórz klasę
Ksiazkaz atrybutami: tytuł, autor, rok_wydania, liczba_stron.
Konstruktor init
- Stwórz klasę
Samochodz konstruktorem przyjmującym markę, model i rok_produkcji. - Stwórz klasę
Pracownikz konstruktorem ustawiającym imię, nazwisko, pensję. - Stwórz klasę
Uzytkownikz konstruktorem i domyślnymi wartościami (aktywny=True, rola=”user”). - Stwórz klasę
Produktz konstruktorem walidującym cenę (musi być > 0). - Stwórz klasę
Dataz konstruktorem przyjmującym dzień, miesiąc, rok i walidującym je.
Metody
- Stwórz klasę
Licznikz metodami zwiększ(), zmniejsz(), reset(), pokaz_wartosc(). - Stwórz klasę
Temperaturaz metodą konwertującą Celsjusz na Fahrenheit. - Stwórz klasę
Hasloz metodą sprawdzającą siłę hasła (długość, znaki specjalne). - Stwórz klasę
Emailz metodą walidującą adres email (@, kropka). - Stwórz klasę
Kalkulatorz metodami: dodaj, odejmij, pomnoz, podziel.
Metody str i repr
- Dodaj
__str__do klasyOsobawyświetlający „Imię Nazwisko, wiek: X lat”. - Dodaj
__str__i__repr__do klasyProdukt. - Stwórz klasę
Czas(godzina, minuta) z__str__formatującym na „HH:MM”. - Stwórz klasę
Dataz__str__formatującym na „DD/MM/YYYY”. - Stwórz klasę
Ulamek(licznik, mianownik) z czytelnym__str__.
Atrybuty prywatne
- Stwórz klasę
KontoBankowez prywatnym atrybutem __saldo. - Dodaj do
KontoBankowemetody wplata() i wyplata() z walidacją. - Stwórz klasę
Hasloz prywatnym atrybutem __haslo i metodą sprawdz(). - Stwórz klasę
Uzytkownikz prywatnym __email i publiczną metodą get_email(). - Stwórz klasę
Sejfz prywatnym kodem i metodą otwierania.
Metody magiczne – porównania
- Dodaj
__eq__do klasyOsoba(porównanie po wieku). - Dodaj
__lt__i__gt__do klasyProdukt(porównanie po cenie). - Stwórz klasę
Ocenaz metodami porównania (__eq__,__lt__,__gt__). - Stwórz klasę
Dataz możliwością porównywania dat. - Stwórz klasę
Prostokatz porównywaniem pól powierzchni.
Metody magiczne – operatory
- Dodaj
__add__do klasyWektor2D(dodawanie wektorów). - Dodaj
__mul__do klasyWektor2D(mnożenie przez skalar). - Stwórz klasę
Czasz możliwością dodawania (__add__). - Stwórz klasę
Pieniadzez możliwością dodawania i odejmowania. - Stwórz klasę
Macierz2x2z operacjami dodawania i mnożenia.
Metody magiczne – kolekcje
- Stwórz klasę
Listaz__len__zwracającym liczbę elementów. - Dodaj
__getitem__do klasyLista(dostęp przez indeks). - Dodaj
__setitem__do klasyLista(ustawianie przez indeks). - Stwórz klasę
Slownikz__getitem__i__setitem__. - Stwórz klasę
Playlistaz__len__i iteracją po utworach.
@property
- Stwórz klasę
Prostokatz @property dla pola powierzchni. - Stwórz klasę
Koloz @property dla pola i obwodu. - Stwórz klasę
Osobaz @property dla pełnego imienia (imię + nazwisko). - Stwórz klasę
Temperaturaz @property dla Fahrenheita (obliczane z Celsjusza). - Stwórz klasę
Produktz @property i setterem dla ceny (walidacja > 0).
Dziedziczenie – podstawy
- Stwórz klasę bazową
Zwierzei klasy potomnePies,Kot. - Stwórz klasę
Pojazdi klasy potomneSamochod,Motocykl. - Stwórz klasę
Figurai klasy potomneKwadrat,Kolo. - Stwórz klasę
Osobai klasy potomneStudent,Pracownik. - Stwórz klasę
Kontoi klasy potomneKontoOszczednosciowe,KontoBiezace.
Dziedziczenie z super()
- W klasie
Samochod(dziedziczyPojazd) użyj super() w konstruktorze. - Nadpisz metodę w klasie potomnej i wywołaj metodę rodzica przez super().
- Stwórz łańcuch dziedziczenia:
Zwierze→Ssak→Pies, użyj super(). - Rozszerz konstruktor klasy potomnej o dodatkowe atrybuty używając super().
- Stwórz klasę
PracownikZPremiądziedziczącąPracowniki rozszerzającą metodę oblicz_wynagrodzenie().
Kompozycja
- Stwórz klasę
Silniki klasęSamochodzawierającą obiekt Silnik. - Stwórz klasę
Adresi klasęOsobazawierającą obiekt Adres. - Stwórz klasę
Telefoni klasęKontaktzawierającą listę telefonów. - Stwórz klasę
Skladniki klasęPrzepiszawierającą listę składników. - Stwórz klasę
Graczi klasęDruzynazawierającą listę graczy.
Klasy abstrakcyjne
- Stwórz abstrakcyjną klasę
Figuraz metodą abstrakcyjną pole(). - Stwórz abstrakcyjną klasę
Platnoscz metodą abstrakcyjną przetworz(). - Stwórz abstrakcyjną klasę
Zwierzez metodą abstrakcyjną dzwiek(). - Stwórz abstrakcyjną klasę
Pojazdz metodami abstrakcyjnymi jedz() i zatrzymaj(). - Stwórz abstrakcyjną klasę
Bazaz metodami abstrakcyjnymi polacz() i rozlacz().
isinstance() i type()
- Sprawdź isinstance() dla obiektów różnych klas z dziedziczeniem.
- Napisz funkcję przyjmującą tylko obiekty typu Prostokat (walidacja isinstance).
- Napisz funkcję działającą różnie dla różnych typów (isinstance).
- Sprawdź czy obiekt jest instancją klasy bazowej lub potomnej.
- Użyj isinstance() do walidacji argumentów metody.
Zadania praktyczne – Biblioteka
- Stwórz klasę
Ksiazkaz atrybutami i metodą__str__. - Stwórz klasę
Bibliotekaprzechowującą listę książek. - Dodaj metody dodaj_ksiazke(), usun_ksiazke() do Biblioteki.
- Dodaj metodę wyszukaj_po_tytule() do Biblioteki.
- Dodaj metodę wyszukaj_po_autorze() do Biblioteki.
- Dodaj atrybut wypozyczona do Ksiazki i metody wypozycz()/zwroc().
- Dodaj do Biblioteki metodę dostepne_ksiazki().
- Dodaj licznik wypożyczeń do Ksiazki.
- Dodaj metodę najpopularniejsza_ksiazka() do Biblioteki.
- Zapisz bibliotekę do pliku JSON.
Zadania praktyczne – Sklep
- Stwórz klasę
Produkt(nazwa, cena, ilosc_w_magazynie). - Stwórz klasę
Koszykprzechowującą produkty do zakupu. - Dodaj metody dodaj_do_koszyka(), usun_z_koszyka().
- Dodaj metodę oblicz_wartosc_koszyka().
- Dodaj metodę zrealizuj_zamowienie() (zmniejsza stan magazynowy).
- Dodaj kategorie produktów (kompozycja lub dziedziczenie).
- Dodaj kody rabatowe (klasa Rabat).
- Dodaj historię zamówień (klasa Zamowienie).
- Stwórz system logowania (klasa Uzytkownik).
- Dodaj oceny produktów.
Zadania praktyczne – Gra RPG
- Stwórz klasę
Postac(imię, hp, atak, obrona). - Dodaj metody atakuj() i otrzymaj_obrazenia().
- Stwórz klasy
Wojownik,Mag,Lucznikdziedziczące po Postac. - Dodaj system poziomów i doświadczenia.
- Dodaj ekwipunek (kompozycja – klasa Przedmiot).
- Dodaj broń zwiększającą atak (klasa Bron).
- Dodaj zbroje zwiększające obronę (klasa Zbroja).
- Dodaj umiejętności specjalne dla każdej klasy postaci.
- Stwórz system walki turowej.
- Dodaj zapisywanie stanu gry do pliku.
Zadania zaawansowane
- Stwórz system zarządzania studentami i kursami (wiele klas współpracujących).
- Stwórz symulator banku z różnymi typami kont.
- Stwórz system rezerwacji hotelu (pokoje, rezerwacje, goście).
- Stwórz prosty system CMS (artykuły, autorzy, kategorie).
- Stwórz system zarządzania projektami (projekty, zadania, użytkownicy).
- Stwórz symulator farmy (rośliny, zwierzęta, zasoby).
- Stwórz system biblioteki muzycznej (utwory, albumy, artyści, playlisty).
- Stwórz prosty system e-learningowy (kursy, lekcje, quizy).
- Stwórz system zarządzania restauracją (menu, zamówienia, stoły).
- Stwórz grę tekstową z systemem lokacji, przedmiotów i postaci.