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.

Struktura klasy

  1. 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.
  2. Atrybuty to zmienne, kt贸re s膮 powi膮zane z klas膮 i kt贸re przechowuj膮 informacje o stanie konkretnego obiektu.
  3. 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:

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:

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:

ksiazka1 = Ksiazka("Pan Tadeusz", "Adam Mickiewicz", "1234567890")
ksiazka2 = Ksiazka("Lalka", "Boles艂aw Prus", "0987654321")
ksiazka1.wypozycz()
ksiazka2.zwroc()

Enkapsulacja (ukrywanie szczeg贸艂贸w implementacji):

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:

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:

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:

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:

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:

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

Spis Tre艣ci

    Klasy i obiekty
    1. Struktura klasy
    2. Przyk艂ad u偶ycia
    3. Dost臋p i modyfikacja atrybut贸w w obiektach
    4. Dekoratory @property i @nazwa_atrybutu.setter
    5. Potrzeba u偶ywania klas i obiekt贸w
      1. Modularno艣膰 i organizacja kodu:
      2. Mo偶liwo艣膰 ponownego u偶ycia kodu:
      3. Enkapsulacja (ukrywanie szczeg贸艂贸w implementacji):
      4. Dziedziczenie:
      5. Polimorfizm:
    6. Pola i metody statyczne oraz klasowe
      1. Pola i metody statyczne
      2. Metody klasowe
    7. R贸偶nice mi臋dzy metodami instancyjnymi, klasowymi i statycznymi
      1. Metody instancyjne:
      2. Metody klasowe:
      3. Metody statyczne:
    8. Zastosowanie w praktyce