Last modified: September 21, 2024
This article is written in: 馃嚨馃嚤
Klasy i obiekty
Programowanie obiektowe (ang. Object-Oriented Programming, OOP) to paradygmat programowania, kt贸ry opiera si臋 na koncepcji "obiekt贸w". Obiekty s膮 instancjami klas, kt贸re 艂膮cz膮 dane (atrybuty) i funkcje (metody) w jedn膮 jednostk臋. Klasy i obiekty s膮 podstawowymi elementami tego paradygmatu i przynosz膮 wiele korzy艣ci w tworzeniu skalowalnego, zrozumia艂ego i 艂atwego do utrzymania kodu.
- Klasa to nic innego jak szablon lub projekt, kt贸ry definiuje struktur臋 i zachowanie obiekt贸w, okre艣laj膮c, jakie atrybuty i metody b臋d膮 dost臋pne dla obiekt贸w tej klasy.
- Kiedy tworzony jest obiekt, czyli konkretna instancja klasy, otrzymuje on rzeczywiste warto艣ci atrybut贸w zdefiniowanych w tej klasie.
Struktura klasy
- Je艣li chodzi o struktur臋 klasy, to konstruktor (
__init__
) jest specjaln膮 metod膮 wywo艂ywan膮 w momencie tworzenia nowego obiektu. Jej g艂贸wn膮 funkcj膮 jest inicjowanie atrybut贸w obiektu, co pozwala nada膰 mu konkretne cechy. - Atrybuty to zmienne, kt贸re s膮 powi膮zane z klas膮 i kt贸re przechowuj膮 informacje o stanie konkretnego obiektu.
- Z kolei metody to funkcje zwi膮zane z klas膮, kt贸re operuj膮 na atrybutach lub wykonuj膮 inne operacje, zale偶ne od tego, co dany obiekt powinien robi膰.
Przyk艂ad u偶ycia
class Osoba:
# Konstruktor klasy
def __init__(self, imie, nazwisko):
self.imie = imie
self.nazwisko = nazwisko
# Metoda klasy
def przedstaw_sie(self):
print(f"Cze艣膰, jestem {self.imie} {self.nazwisko}")
# Tworzenie obiekt贸w klasy Osoba
osoba1 = Osoba("Jan", "Kowalski")
osoba2 = Osoba("Adam", "Nowak")
# Wywo艂anie metody dla obiekt贸w
osoba1.przedstaw_sie()
osoba2.przedstaw_sie()
W powy偶szym kodzie:
- Zdefiniowali艣my klas臋
Osoba
z dwoma atrybutami (imie
inazwisko
) oraz jedn膮 metod膮 (przedstaw_sie
). - U偶ywaj膮c klasy, stworzyli艣my dwa obiekty:
osoba1
iosoba2
. - Dla ka偶dego obiektu wywo艂ali艣my metod臋
przedstaw_sie
.
Dost臋p i modyfikacja atrybut贸w w obiektach
W programowaniu obiektowym atrybuty (zmienne) s膮 przechowywane w obiektach. Aby uzyska膰 dost臋p do tych atrybut贸w, korzysta si臋 z notacji kropkowej:
nazwa_obiektu.atrybut
Atrybuty mog膮 by膰 modyfikowane podobnie jak zwyk艂e zmienne:
osoba = Osoba("Jan", "Kowalski")
print(osoba.imie) # Wy艣wietli: Jan
osoba.imie = "Adam"
print(osoba.imie) # Wy艣wietli: Adam
Dekoratory @property i @nazwa_atrybutu.setter
Aby wprowadzi膰 kontrol臋 nad dost臋pem do atrybut贸w oraz modyfikacj膮 ich warto艣ci, w Pythonie mo偶na u偶ywa膰 dekorator贸w @property
oraz @nazwa_atrybutu.setter
. Te dekoratory pozwalaj膮 na tworzenie funkcji, kt贸re s膮 wywo艂ywane podczas odczytu i modyfikacji atrybutu.
class Osoba:
def __init__(self, imie, nazwisko):
self._imie = imie # Konwencja z podkre艣lnikiem wskazuje na "chronione" atrybuty
self._nazwisko = nazwisko
@property
def imie(self):
print('Kto艣 pr贸buje odczyta膰 imi臋')
return self._imie
@imie.setter
def imie(self, nowa_wartosc):
print('Kto艣 modyfikuje imi臋')
self._imie = nowa_wartosc
W klasie Osoba
, atrybuty _imie
i _nazwisko
s膮 oznaczone jednym podkre艣lnikiem, co jest konwencj膮 wskazuj膮c膮, 偶e atrybuty te s膮 "chronione" i nie powinny by膰 modyfikowane bezpo艣rednio z zewn膮trz klasy. Dekoratory @property
i @imie.setter
umo偶liwiaj膮 odpowiednio odczyt i modyfikacj臋 warto艣ci atrybutu _imie
.
osoba = Osoba("Jan", "Kowalski")
print(osoba.imie) # Kto艣 pr贸buje odczyta膰 imi臋, wy艣wietli: Jan
osoba.imie = "Adam" # Kto艣 modyfikuje imi臋
print(osoba.imie) # Kto艣 pr贸buje odczyta膰 imi臋, wy艣wietli: Adam
Potrzeba u偶ywania klas i obiekt贸w
Modularno艣膰 i organizacja kodu:
- Klasy pozwalaj膮 na grupowanie powi膮zanych danych i funkcji, co pomaga w organizacji kodu. Kod staje si臋 bardziej modularny, co u艂atwia jego zrozumienie i utrzymanie.
- Przyk艂ad: W aplikacji do zarz膮dzania bibliotek膮 mo偶emy mie膰 klas臋
Ksiazka
, kt贸ra 艂膮czy atrybuty (tytu艂, autor, ISBN) z metodami (wypo偶ycz, zwr贸膰).
class Ksiazka:
def __init__(self, tytul, autor, isbn):
self.tytul = tytul
self.autor = autor
self.isbn = isbn
def wypozycz(self):
print(f'Ksi膮偶ka "{self.tytul}" zosta艂a wypo偶yczona.')
def zwroc(self):
print(f'Ksi膮偶ka "{self.tytul}" zosta艂a zwr贸cona.')
Mo偶liwo艣膰 ponownego u偶ycia kodu:
- Klasy umo偶liwiaj膮 tworzenie obiekt贸w o podobnej strukturze i zachowaniu, co zapobiega duplikacji kodu.
- Przyk艂ad: Mo偶emy utworzy膰 wiele obiekt贸w klasy
Ksiazka
bez konieczno艣ci wielokrotnego definiowania tych samych atrybut贸w i metod.
ksiazka1 = Ksiazka("Pan Tadeusz", "Adam Mickiewicz", "1234567890")
ksiazka2 = Ksiazka("Lalka", "Boles艂aw Prus", "0987654321")
ksiazka1.wypozycz()
ksiazka2.zwroc()
Enkapsulacja (ukrywanie szczeg贸艂贸w implementacji):
- Klasy umo偶liwiaj膮 ukrywanie wewn臋trznych szczeg贸艂贸w implementacji przed u偶ytkownikami obiekt贸w. Dzi臋ki temu u偶ytkownicy mog膮 korzysta膰 z obiekt贸w bez znajomo艣ci ich wewn臋trznej struktury.
- Przyk艂ad: W klasie
Ksiazka
mo偶emy ukry膰 wewn臋trzne szczeg贸艂y dotycz膮ce statusu wypo偶yczenia ksi膮偶ki.
class Ksiazka:
def __init__(self, tytul, autor, isbn):
self.tytul = tytul
self.autor = autor
self.isbn = isbn
self._wypozyczona = False
def wypozycz(self):
if not self._wypozyczona:
self._wypozyczona = True
print(f'Ksi膮偶ka "{self.tytul}" zosta艂a wypo偶yczona.')
else:
print(f'Ksi膮偶ka "{self.tytul}" jest ju偶 wypo偶yczona.')
def zwroc(self):
if self._wypozyczona:
self._wypozyczona = False
print(f'Ksi膮偶ka "{self.tytul}" zosta艂a zwr贸cona.')
else:
print(f'Ksi膮偶ka "{self.tytul}" nie by艂a wypo偶yczona.')
Dziedziczenie:
- Dziedziczenie pozwala na tworzenie nowych klas na podstawie istniej膮cych klas, co umo偶liwia ponowne u偶ycie kodu i rozszerzanie jego funkcjonalno艣ci.
- Przyk艂ad: Mo偶emy stworzy膰 klas臋
Ebook
, kt贸ra dziedziczy po klasieKsiazka
i dodaje nowe atrybuty oraz metody specyficzne dla e-book贸w.
class Ebook(Ksiazka):
def __init__(self, tytul, autor, isbn, rozmiar_pliku):
super().__init__(tytul, autor, isbn)
self.rozmiar_pliku = rozmiar_pliku
def pobierz(self):
print(f'Pobieranie e-booka "{self.tytul}". Rozmiar pliku: {self.rozmiar_pliku}MB')
Polimorfizm:
- Polimorfizm pozwala na traktowanie obiekt贸w r贸偶nych klas w ten sam spos贸b, pod warunkiem 偶e klasy te dziel膮 wsp贸lny interfejs (np. dziedzicz膮 po tej samej klasie bazowej).
- Przyk艂ad: Mo偶emy u偶ywa膰 tej samej metody
wypozycz
dla obiekt贸w klasyKsiazka
iEbook
.
ksiazka = Ksiazka("Pan Tadeusz", "Adam Mickiewicz", "1234567890")
ebook = Ebook("Lalka", "Boles艂aw Prus", "0987654321", 5)
ksiazka.wypozycz()
ebook.wypozycz()
ebook.pobierz()
Pola i metody statyczne oraz klasowe
W programowaniu obiektowym cz臋sto korzysta si臋 z p贸l i metod statycznych oraz klasowych. R贸偶ni膮 si臋 one od standardowych p贸l i metod instancji, gdy偶 s膮 powi膮zane bezpo艣rednio z klas膮, a nie z jej instancjami.
Pola i metody statyczne
Pola i metody statyczne s膮 zwi膮zane z klas膮, a nie z konkretnymi obiektami tej klasy. W Pythonie metody statyczne s膮 tworzone przy u偶yciu dekoratora @staticmethod
. Dost臋p do nich jest mo偶liwy zar贸wno poprzez nazw臋 klasy, jak i przez obiekt tej klasy. Metody statyczne nie maj膮 dost臋pu do atrybut贸w instancji ani do atrybut贸w klasy, poniewa偶 nie przyjmuj膮 jako pierwszego argumentu self
ani cls
.
Przyk艂ad:
class Czlowiek:
liczba_glow = 1
@staticmethod
def wyswietl_glowy():
print(f'Liczba g艂贸w: {Czlowiek.liczba_glow}')
Czlowiek.wyswietl_glowy() # Liczba g艂贸w: 1
przykladowy_czlowiek = Czlowiek()
przykladowy_czlowiek.wyswietl_glowy() # Liczba g艂贸w: 1
W powy偶szym przyk艂adzie, metoda wyswietl_glowy
jest metod膮 statyczn膮. Mo偶na j膮 wywo艂a膰 zar贸wno poprzez klas臋 Czlowiek
, jak i przez jej instancj臋 przykladowy_czlowiek
.
Metody klasowe
Metody klasowe stanowi膮 rozszerzenie metod statycznych. S膮 one tworzone przy u偶yciu dekoratora @classmethod
i ich pierwszym parametrem jest cls
, kt贸ry reprezentuje sam膮 klas臋 (podobnie jak self
reprezentuje instancj臋). Metody klasowe mog膮 dost臋powa膰 do p贸l klasowych oraz do innych metod klasowych.
Przyk艂ad:
class Czlowiek:
liczba_glow = 1
@classmethod
def wyswietl_glowy(cls):
print(f'Liczba g艂贸w: {cls.liczba_glow}') # Uwaga: u偶ywamy cls
, a nie nazwy klasy!
def zwykla_funkcja(self):
self.wyswietl_glowy()
Czlowiek.wyswietl_glowy() # Liczba g艂贸w: 1
przykladowy_czlowiek = Czlowiek()
przykladowy_czlowiek.wyswietl_glowy() # Liczba g艂贸w: 1
przykladowy_czlowiek.zwykla_funkcja() # Liczba g艂贸w: 1
W klasie Czlowiek
mamy pole klasowe liczba_glow
, metod臋 klasow膮 wyswietl_glowy()
, kt贸ra korzysta z tego pola, oraz metod臋 instancyjn膮 zwykla_funkcja()
. W przyk艂adzie pokazano r贸偶ne sposoby wywo艂ywania metody klasowej, zar贸wno bezpo艣rednio z poziomu klasy, jak i z poziomu instancji. Kluczow膮 r贸偶nic膮 jest to, 偶e w metodach klasowych u偶ywa si臋 cls
do odwo艂ywania si臋 do klasowych atrybut贸w i metod, podczas gdy w metodach instancyjnych u偶ywa si臋 self
.
R贸偶nice mi臋dzy metodami instancyjnymi, klasowymi i statycznymi
Metody instancyjne:
- S膮 powi膮zane z instancj膮 klasy.
- Maj膮 dost臋p do atrybut贸w instancji przez
self
. - Maj膮 dost臋p do atrybut贸w klasy przez
self.__class__
lub bezpo艣rednio przez nazw臋 klasy.
Przyk艂ad:
class Samochod:
def __init__(self, marka, model):
self.marka = marka
self.model = model
def przedstaw_sie(self):
print(f"Samoch贸d: {self.marka} {self.model}")
auto = Samochod("Toyota", "Corolla")
auto.przedstaw_sie() # Samoch贸d: Toyota Corolla
Metody klasowe:
- S膮 powi膮zane z klas膮, nie z instancj膮.
- Maj膮 dost臋p do atrybut贸w klasy przez
cls
. - Nie maj膮 dost臋pu do atrybut贸w instancji bezpo艣rednio.
Przyk艂ad:
class Samochod:
liczba_kol = 4
@classmethod
def wyswietl_liczbe_kol(cls):
print(f"Samochody maj膮 {cls.liczba_kol} ko艂a")
Samochod.wyswietl_liczbe_kol() # Samochody maj膮 4 ko艂a
Metody statyczne:
- Nie s膮 powi膮zane ani z instancj膮, ani z klas膮.
- Nie maj膮 dost臋pu do atrybut贸w instancji ani klasy.
- S膮 u偶ywane do wykonywania zada艅, kt贸re s膮 zwi膮zane z klas膮, ale nie wymagaj膮 dost臋pu do jej stanu.
Przyk艂ad:
class Samochod:
@staticmethod
def informacje_o_samochodach():
print("Samochody to pojazdy mechaniczne s艂u偶膮ce do transportu")
Samochod.informacje_o_samochodach() # Samochody to pojazdy mechaniczne s艂u偶膮ce do transportu
Zastosowanie w praktyce
Pola i metody statyczne oraz klasowe s膮 szczeg贸lnie przydatne w sytuacjach, gdzie operacje dotycz膮 samej klasy, a nie konkretnej instancji. Na przyk艂ad, mog膮 by膰 u偶ywane do zliczania liczby instancji klasy, zarz膮dzania wsp贸lnymi zasobami czy implementowania wzorc贸w projektowych takich jak Singleton.
I. Zliczanie liczby instancji klasy:
class Osoba:
liczba_instancji = 0
def __init__(self, imie):
self.imie = imie
Osoba.liczba_instancji += 1
@classmethod
def ile_instancji(cls):
return cls.liczba_instancji
osoba1 = Osoba("Jan")
osoba2 = Osoba("Anna")
print(Osoba.ile_instancji()) # 2
II. Zarz膮dzanie wsp贸lnymi zasobami:
class BazaDanych:
polaczenie = None
@classmethod
def polacz(cls):
if cls.polaczenie is None:
cls.polaczenie = "Po艂膮czenie do bazy danych"
return cls.polaczenie
polaczenie1 = BazaDanych.polacz()
polaczenie2 = BazaDanych.polacz()
print(polaczenie1) # Po艂膮czenie do bazy danych
print(polaczenie1 is polaczenie2) # True
III. Wzorce projektowe:
class Singleton:
_instancja = None
def __new__(cls, *args, **kwargs):
if not cls._instancja:
cls._instancja = super(Singleton, cls).__new__(cls, *args, **kwargs)
return cls._instancja
s1 = Singleton()
s2 = Singleton()
print(s1 is s2) # True