Objektové programovanie v Pythone

V Pythone je všetko objekt

  • funkcia
  • číslo
  • reťazec
  • modul
  • kolekcia
  • údajový typ

Objekt

je množina dát (atribútov) a množina funkcií (metód) ktoré pracujú s týmito dátami.

+-------------------+  ---> lower(self) -> "ahoj svet!"
| value: Ahoj Svet! | 
+-------------------+  ---> upper(self) -> "AHOJ SVET!"

📚 Oficiálna dokumentácia: Python Data Model · 📖 Dive Into Python 3: Native Datatypes

Životný cyklus objektu

# Vytvorenie
pozdrav = "Dobré ráno"

# Použitie
print(pozdrav.upper())

# Zničenie
del pozdrav

Explicitné zničenie objektu je nepovinné, Python sa o to stará sám.

📚 Oficiálna dokumentácia: objects, values and types · 📖 Dive Into Python 3: Native Datatypes

Referencie na objekt

Na jeden objekt je môže existovať viacero referencií.

pozdrav1 ──┐
           ├──> ┌───────────────────┐
pozdrav2 ──┘    │ objekt typu str   │
                │ "Dobré ráno"      │
                └───────────────────┘

Obe premenné obsahujú referenciu na ten istý objekt.
pozdrav1 = "Dobré ráno"
pozdrav2 = pozdrav1  # obe referencie ukazujú na rovnaký objekt

📚 Oficiálna dokumentácia: assignment statements · 📖 Dive Into Python 3: Native Datatypes

Garbage collector

  • mechanizmus, ktorý sa automaticky stará o uvoľňovanie pamäte.
  • počíta referencie a uvoľní pamäť na ktorú nie je žiadna referencia
  • Čas automatického uvoľňovania pamäte je riadený garbage collectorom a nie je predvídateľný.
import gc

data = [1, 2, 3]
del data                 # odstráni sa referencia na zoznam
print(gc.collect())      # ručné spustenie je zvyčajne zbytočné

📚 Oficiálna dokumentácia: gc — garbage collector

Priradenie je vytvorenie novej referencie

list1 = [1, 2, 3]
list2 = list1  # obe referencie ukazujú na rovnaký objekt
list1.append(4) # Objekt sa zmení
print(list1)  # [1, 2, 3, 4]
print(list2)  # [1, 2, 3, 4]

Nemeniteľné a meniteľné objekty

  • Mutable objekty sa dajú meniť: list, dict, set
  • Inmutable objekty sa nedajú meniť : int, float, str, tuple
cisla = [1, 2]
cisla.append(3)       # mení pôvodný zoznam

pozdrav = "Ahoj"
pozdrav += " svet"    # vytvorí nový reťazec

print(cisla)    # [1, 2, 3]
print(pozdrav)  # Ahoj svet

📚 Oficiálna dokumentácia: built-in types · 📖 Dive Into Python 3: Native Datatypes

Triedy sú šablóny pre vytvorenie objektov

V triede definujeme:

  • spôsob inicializácie (konštruktor)
  • attibúty (dáta)
  • metódy (funkcie pre prácu s dátami)
  • spôsob uvoľňovania zdrojov (deštruktor)

📚 Oficiálna dokumentácia: classes · 📖 Dive Into Python 3: Classes & Iterators

Introspekcia

  • Každý údajový typ má svoju triedu
  • Každá trieda je zároveň objekt
hodnota = "Ahoj"

print(type(hodnota))          # <class 'str'>
print(isinstance(hodnota, str))  # True
print(hasattr(hodnota, "upper")) # True

📚 Oficiálna dokumentácia: classes as objects · 📖 Dive Into Python 3: Classes & Iterators

class definuje novú triedu

  • __init__ je konštruktor, ktorý sa volá pri vytvorení objektu. Definuje počiatočné hodnoty atribútov.
  • self je referencia na aktuálny objekt.
  • inštancia = trieda s konkrétnymi hodnotami atributov.
class Dog:
    def __init__(self, name):
        # Name je atribút
        self.name = name

dog1 = Dog("Buddy")

Metódy

Opisujú správanie objektu

menia hodnoty atribútov a spôsobujú vedľajšie efekty

class Lamp:
    def __init__(self):
        self.svieti = False

    def zapni(self):
        self.svieti = True

lamp = Lamp()
lamp.zapni()
print(lamp.svieti)  # True

📚 Oficiálna dokumentácia: method objects · 📖 Dive Into Python 3: Classes & Iterators

Deštruktor sa stará o uvoľnenie zrojov.

  • __init__ → inicializácia objektu
  • __del__ → uvoľnenie (zriedka používané)

📚 Oficiálna dokumentácia: object initialization and finalization · 📖 Dive Into Python 3: Classes & Iterators

class Person:
    def __init__(self, name):
        self.name = name

    # deštruktor väčšinou v Pythone nie je potrebný
    def __del__(self):
        print("Objekt zmazaný")

Statické metódy nepotrebujú referenciu na objekt, ale súvisia s triedou.

Vyznačíme ich pomocou dekorátora

class Math:
    @staticmethod
    def add(a, b):
        return a + b

print(Math.add(2, 3))  # 5

Definovanie operátorov

  • '+ - [] in'
  • Špeciálne metódy s __
class Vector:
    def __init__(self, x):
        self.x = x

    def __add__(self, other):
        return Vector(self.x + other.x)

vysledok = Vector(2) + Vector(3)
print(vysledok.x)  # 5

📚 Oficiálna dokumentácia: special method names · 📖 Dive Into Python 3: Special Method Names

Iterátory

  • Zložitejší spôsob zápisu generátorov
  • Vyzerajú ako zoznamy, ale neukladajú všetky prvky v pamäti

priklad ako použiť iterátor

counter = Counter(5)
for num in counter:
    print(num)

📚 Oficiálna dokumentácia: iter() and next() · 📖 Dive Into Python 3: Classes & Iterators

Rozhranie iterátora

  • Implementujú rozhranie:

    • __iter__()
    • __next__() vyhadzuje výnimku StopIteration, keď sa prvky minú

📚 Oficiálna dokumentácia: iterator protocol · 📖 Dive Into Python 3: Classes & Iterators

class Counter:
    def __init__(self, max):
        self.max = max
        self.current = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.current < self.max:
            self.current += 1
            return self.current
        raise StopIteration

📚 Oficiálna dokumentácia: __iter__() and __next__() · 📖 Dive Into Python 3: Classes & Iterators

Duck Typing a polymorfizmus

  • objekty rôznych typov sú rovnaké, pokiaľ majú rovnaké metódy alebo atribúty.
  • V Pythone sa nevyžaduje explicitné deklarovanie rozhraní, stačí, aby objekt implementoval požadované metódy.
  • Funkcia môže prijímať argumenty rôznych typov a správať sa odlišne v závislosti od typu argumentu.
  • Polymorfizmus je schopnosť objektov rôznych typov reagovať na rovnakú metódu alebo operáciu.
class Bird:
    def fly(self):
        print("Vták lieta.")

class Airplane:
    def fly(self):
        print("Lietadlo lieta.")

def make_it_fly(thing):
    thing.fly()

make_it_fly(Bird())
make_it_fly(Airplane())

Zapuzdrenie - súkromné atribúty a metódy

Pomocou _ (podtržítko) môžeme označiť atribúty a metódy ako súkromné.

class Person:
    def __init__(self, name):
        self._name = name  # súkromný atribút

    def pozdrav(self):
        return f"Ahoj, {self._name}!"

osoba = Person("Jana")
print(osoba.pozdrav())  # Ahoj, Jana!

Skryjeme tak implementačné detaily a zabraňujeme priamemu prístupu k nim z vonkajšieho kódu.

Dedičnosť

Vytovríme novú triedy pridaním nových atribútov a metód ku pôvodnej triede.

class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        pass

class Dog(Animal):
    def speak(self):
        return "Haf!"

📚 Oficiálna dokumentácia: inheritance · 📖 Dive Into Python 3: Classes & Iterators

Abstraktná metóda je priamo určená na prekrytie.

class Animal:
    def speak(self):
        return "Neznámy zvuk"

class Cat(Animal):
    def speak(self):       # prekrytie metódy rodiča
        return "Mňau!"

print(Animal().speak())  # Neznámy zvuk
print(Cat().speak())     # Mňau!

Rozširenie pomocu kompozície

Príklad:

class Battery:
    def energy(self):
        return "Batéria dodáva energiu."

class Phone:
    def __init__(self, battery):
        self.battery = battery

    def turn_on(self):
        return self.battery.energy() + " Telefón je zapnutý."

print(Phone(Battery()).turn_on())

Dedičnosť a kompozícia

Pri vytváraní novej triedy vieme využiť existujúce triedy (dedičnosť) a rozšíriť ich o nové atribúty a metódy.

Inheritance vs Composition

  • Dedičnosť (Inheritance) – vytvárame novú triedu na základe existujúcej triedy. Nová trieda dedí atribúty a metódy z rodičovskej triedy a môže ich rozšíriť alebo prepísať.
  • Kompozícia (Composition) – vytvárame novú triedu, ktorá obsahuje iné objekty ako svoje atribúty. Nová trieda využíva funkcionalitu týchto objektov a môže ich kombinovať alebo rozširovať.

📚 Oficiálna dokumentácia: inheritance · 📖 Dive Into Python 3: Classes & Iterators

Príklad kompozície:

class Engine:
    def start(self):
        print("Motor sa spustil.")

class Car:
    def __init__(self):
        self.engine = Engine()

    def start(self):
        self.engine.start()

📚 Oficiálna dokumentácia: class objects · 📖 Dive Into Python 3: Classes & Iterators

Bezpečné spracovanie chýb počas behu programu

Funkcia sa môže skončiť neočakávaným spôsobom:

  • súbor sa nenašiel
  • spojenie sa nepodarilo
  • Operácia trvá príliš dlho
  • nie je dosť zdrojov.

📚 Oficiálna dokumentácia: errors and exceptions · 📖 Dive Into Python 3: Exceptions

Výnimky (Exceptions)

Výnimka je spôsob hlásenia chyby pri vykonávaní funkcie ktorý obchádza návratovú hodnotu.

Funkcia s výnimkami má potom dva rôzne spôsoby ukončenia:

  • návratová hodnota.
  • výnimka, návratová hodnmota nie je k dispozícii.

Ako sa výnimky spracúvajú?

  • Ak nastane chyba, skript neskončí – reagujeme podľa typu chyby
try:
    x = int("abc")
except ValueError:
    print("Konverzia zlyhala")
try:
    vysledok = 10 / 0
except ZeroDivisionError:
    print("Chyba: delenie nulou!")

📚 Oficiálna dokumentácia: handling exceptions · 📖 Dive Into Python 3: Exceptions

Chytanie viacerých chýb naraz

Druh výnimky určíme podľa jej typu (triedy)

try:
    x = int(input("Zadaj číslo: "))
    print(10 / x)
except ValueError:
    print("Chyba: zadali ste niečo iné ako číslo.")
except ZeroDivisionError:
    print("Chyba: nedelíš nulou!")

Všeobecný except chytí všetky výninky

Všetky výnimky sú potomkami Exception

try:
    x = int(input("Zadaj číslo: "))
    print(10 / x)
except Exception as e:
    print(f"Nečakána chyba: {e}")

👉 Využíva sa ako záložná možnosť. 👉 Neodporúča sa chytall Exception bez potreby — môže zakryť iné chyby.

Blok finally

try:
    f = open("data.txt")
    data = f.read()
except FileNotFoundError:
    print("Súbor nebol nájdený.")
finally:
    print("Ukončenie operácie.")

Blok finally sa vykoná vždy — či už nastala chyba, alebo nie. Používa sa na upratovanie (zatvorenie súboru, spojenia, ...).

Úplný tvar

try:
    # kód, ktorý môže zlyhať
    vysledok = 10 / x
except ZeroDivisionError:
    # ak nastane konkrétna chyba
    print("Delenie nulou!")
else:
    # ak nič nezlyhalo
    print(f"Výsledok: {vysledok}")
finally:
    # toto sa vykoná vždy
    print("Hotovo.")

Poradie: try → except → else → finally

📚 Oficiálna dokumentácia: try statement · 📖 Dive Into Python 3: Exceptions

raise – vyvolanie výnimky

vek = int(input("Zadaj vek: "))

if vek < 0:
    raise ValueError("Vek nemôže byť záporný.")

print(f"Máš {vek} rokov.")

Výstup pri zápornom veku:

Traceback (most recent call last):
  ...
ValueError: Vek nemôže byť záporný.

📚 Oficiálna dokumentácia: raise · 📖 Dive Into Python 3: Exceptions

Kedy použiť raise?

  • Ak je vstup neplatný alebo stav nedetekovateľný inak.
  • Ak chceme explicitne hlásiť chybu vo funkcii.
def nastav_vek(vek):
    if vek < 0:
        raise ValueError("Vek musí byť nezáporné číslo.")

Vlastné výnimky

Vieme definovať vlastných potomkov Exception . Vo výnimke je uložená informácia o vzniknutom stave - chybové hlásenie

Podľa vzniknutej situácie vieme:

  • zaznamenať alebo hlásiť správu o chybe
  • Ukončiť program
  • ignorovať situáciu
  • pokúsiť sa korigovať chybu a pokračovať
class NedostatokPenazizError(Exception):
    pass

def plat(suma):
    if suma > 100:
        raise NedostatokPenaziError("Nane love.")

📚 Oficiálna dokumentácia: user-defined exceptions · 📖 Dive Into Python 3: Exceptions

Hierarchia vlastných výnimiek

class AppError(Exception):
    pass

class NedostatokPenezError(AppError):
    pass

class ChybaAutentifikacie(AppError):
    pass

Chytíme všetky chyby aplikácie:

try:
    ...
except AppError as e:
    print(f"Aplikačná chyba: {e}")

📚 Oficiálna dokumentácia: built-in exceptions · 📖 Dive Into Python 3: Exceptions

Literatúra

Dive Into Python 3, časť o výnimkách

Zdroje na prečítanie

  • Oficiálna dokumentácia — Exceptions: https://docs.python.org/3/tutorial/errors.html
  • Exception Hierarchy: https://docs.python.org/3/library/exceptions.html
  • Built-in Exceptions: https://docs.python.org/3/library/exceptions.html#built-in-exceptions
Reload?