Osztályok a Pythonban

Mi az az osztály (class)?

Az osztály a Python objektumorientált programozásának alapvető építőeleme. Egy osztály olyan mint egy tervrajz vagy sablon, amely alapján objektumokat hozhatunk létre.

Újrafelhasználható

Egy osztályból több példányt is létrehozhatunk

Adatbezárás

Az adatok és a rajtuk végzett műveletek egy egységbe zárva

Öröklődés

Új osztályokat építhetünk meglévőkből

Első osztályunk: Autó

Nézzük meg egy egyszerű osztály létrehozását Python kódban:

class Auto:
    """Ez egy egyszerű autó osztály."""

    def __init__(self, marka, szin):
        self.marka = marka  # Az autó márkája
        self.szin = szin    # Az autó színe

    def bemutatkozas(self):
        print(f"Ez egy {self.szin} színű {self.marka}.")

# Példányosítás és használat
my_car = Auto("Toyota", "piros")
my_car.bemutatkozas()  # Kiírja: Ez egy piros színű Toyota.

Próbáld ki!

Osztályok vs. Szótárak

Nézzük meg, mi a különbség egy osztály és egy szótár között:

Osztály

class Szemely:
    def __init__(self, nev, kor):
        self.nev = nev
        self.kor = kor

    def koszon(self):
        return f"Szia, {self.nev} vagyok!"

ember = Szemely("Anna", 25)
print(ember.koszon())

Szótár

szemely = {
    "nev": "Anna",
    "kor": 25
}

# Nincs beépített metódus
print(f"Szia, {szemely['nev']} vagyok!")

Hasonlítsd össze!

Osztályok Alapjai

Osztály létrehozása

Az osztályokat a class kulcsszóval hozzuk létre. Az osztály egy tervrajz, ami alapján objektumokat (példányokat) készíthetünk.

# Egyszerű osztály létrehozása
class Kutya:
    # Osztályváltozó - minden példány között megosztva
    faj = "kutya"

    # Konstruktor metódus
    def __init__(self, nev):
        # Példányváltozó - minden példánynak saját
        self.nev = nev

# Objektum (példány) létrehozása
bodri = Kutya("Bodri")
print(bodri.nev)     # Kiírja: Bodri
print(bodri.faj)     # Kiírja: kutya

Hozz létre egy kutyát!

Osztály vs. Példány

Az osztály és a példány közötti különbség megértése alapvető fontosságú:

Osztály (Class)

  • Tervrajz vagy sablon
  • Meghatározza a szerkezetet
  • Osztályváltozókat tartalmaz
  • Metódusokat definiál

Példány (Instance)

  • Konkrét objektum
  • Saját adatokat tárol
  • Példányváltozókat tartalmaz
  • Használja az osztály metódusait
class Macska:
    # Osztályváltozó
    faj = "macska"

    def __init__(self, nev, kor):
        # Példányváltozók
        self.nev = nev
        self.kor = kor

# Két különböző példány
cirmi = Macska("Cirmi", 3)
mirci = Macska("Mirci", 2)

# Mindkettő ugyanazt az osztályváltozót használja
print(cirmi.faj)   # macska
print(mirci.faj)   # macska

# De különböző példányváltozóik vannak
print(cirmi.nev)   # Cirmi
print(mirci.nev)   # Mirci

Hozz létre két macskát!

Osztályok és memória

Nézzük meg, hogyan tárolódnak az objektumok a memóriában:

Konstruktor és Osztályváltozók

A konstruktor (__init__)

A konstruktor egy speciális metódus (függvény) az osztályban, ami akkor fut le automatikusan, amikor létrehozunk egy új objektumot. Ez olyan, mint egy "előkészítő" függvény: beállítja az objektum kezdeti állapotát, létrehozza és feltölti a szükséges változókat.

Pythonban ezt a __init__ metódus valósítja meg. A self paraméter mindig az aktuális objektumra mutat, amin keresztül el tudjuk érni az objektum saját változóit.

class Jatekos:
    def __init__(self, nev, eletero=100):
        # Példányváltozók inicializálása
        self.nev = nev
        self.eletero = eletero
        self.aktiv = True

    def serules(self, mennyiseg):
        self.eletero -= mennyiseg
        if self.eletero <= 0:
            self.aktiv = False
            print(f"{self.nev} játékos kiesett!")

# Játékos létrehozása
player1 = Jatekos("Harcos", 120)
player2 = Jatekos("Íjász")  # Alapértelmezett életerővel

Hozz létre egy játékost!

Osztályváltozók vs. Példányváltozók

Kétféle változót használhatunk az osztályokban:

  • Osztályváltozók: Ezek olyan változók, amik az osztályhoz tartoznak, nem pedig az egyes objektumokhoz. Minden objektum ugyanazt az értéket látja és használja. Például egy játékban a maximális pontszám vagy egy webshopban az áfa mértéke lehet osztályváltozó.
  • Példányváltozók: Ezek az egyes objektumok saját változói. Minden objektumnak saját, független példánya van belőlük. Például egy játékosnak a saját pontszáma vagy egy terméknek a saját ára.
class Szamolo:
    # Osztályváltozó - minden példány között megosztva
    osszes_szamolas = 0

    def __init__(self, kezdoertek=0):
        # Példányváltozó - minden példánynak saját
        self.ertek = kezdoertek

    def novel(self, mennyiseg):
        self.ertek += mennyiseg
        # Osztályváltozó módosítása
        Szamolo.osszes_szamolas += 1

# Példányok létrehozása
szam1 = Szamolo(10)
szam2 = Szamolo(20)

szam1.novel(5)  # ertek: 15, osszes_szamolas: 1
szam2.novel(10) # ertek: 30, osszes_szamolas: 2

Számlálók kezelése

Első számláló

0

Második számláló

0

Összes művelet:

0

Osztálymetódusok és Statikus metódusok

A normál metódusok (mint amiket eddig láttunk) mindig egy konkrét objektumon működnek, de van két speciális típusú metódus is:

  • Osztálymetódusok (@classmethod): Ezek olyan függvények, amiket úgy jelölünk meg a @classmethod dekorátorral, hogy közvetlenül az osztályon is meghívhatók legyenek (nem kell előtte objektumot létrehozni). Az első paraméterük (cls) az osztályt jelenti, így hozzáférnek az osztályváltozókhoz.
  • Statikus metódusok (@staticmethod): Ezek olyan függvények az osztályon belül, amik nem függnek sem az osztálytól, sem az objektumoktól. Olyan segédfüggvények, amik logikailag az osztályhoz tartoznak, de nem használnak osztály- vagy példányváltozókat.

A dekorátorok (mint a @classmethod és @staticmethod) olyan speciális jelölések Pythonban, amik módosítják a függvények viselkedését. Úgy képzeld el őket, mint egy "címkét" vagy "matricát", ami megmondja Pythonnak, hogy az adott függvényt hogyan kell kezelnie.

class Matematika:
    PI = 3.14159

    @classmethod
    def kor_terulet(cls, sugar):
        """Osztálymetódus - használhatja az osztály adatait"""
        return cls.PI * sugar * sugar

    @staticmethod
    def negyzet_terulet(oldal):
        """Statikus metódus - független az osztálytól"""
        return oldal * oldal

# Használat példányosítás nélkül
print(Matematika.kor_terulet(5))    # Használja a PI osztályváltozót
print(Matematika.negyzet_terulet(4)) # Nem használ osztályváltozót

Geometriai számítások

Metódusok és Tulajdonságok

Metódusok az osztályokban

A metódusok olyan függvények, amelyeket az osztályon belül definiálunk. Ezek határozzák meg, hogy mit tud csinálni az objektumunk. Minden metódus első paramétere a self, ami az aktuális objektumra mutat.

A metódusok segítségével tudjuk:

  • Az objektum adatait módosítani
  • Számításokat végezni az objektum adataival
  • Az objektum viselkedését szabályozni
  • Az objektum állapotát lekérdezni
class BankSzamla:
    def __init__(self, tulajdonos, egyenleg=0):
        self.tulajdonos = tulajdonos
        self.egyenleg = egyenleg

    def befizet(self, osszeg):
        """Pénz befizetése a számlára"""
        if osszeg > 0:
            self.egyenleg += osszeg
            return True
        return False

    def kivesz(self, osszeg):
        """Pénz kivétele a számláról"""
        if osszeg > 0 and osszeg <= self.egyenleg:
            self.egyenleg -= osszeg
            return True
        return False

    def egyenleg_lekerdezes(self):
        """Aktuális egyenleg lekérdezése"""
        return f"{self.egyenleg} Ft"

    def atutal(self, masik_szamla, osszeg):
        """Pénz átutalása másik számlára"""
        if self.kivesz(osszeg):
            masik_szamla.befizet(osszeg)
            return True
        return False

Bankszámla kezelése

Átutalás

Tulajdonságok (Properties)

A tulajdonságok lehetővé teszik, hogy az osztály adataihoz biztonságosan férhessünk hozzá. Segítségükkel:

  • Ellenőrizhetjük és validálhatjuk az értékeket beállításkor
  • Számított értékeket adhatunk vissza lekérdezéskor
  • Elrejthetjük az osztály belső működését
  • Változóként használhatjuk a metódusokat
class Teglalap:
    def __init__(self, hossz, szelesseg):
        self._hossz = hossz        # védett változó
        self._szelesseg = szelesseg

    @property
    def terulet(self):
        """Terület lekérdezése számított értékként"""
        return self._hossz * self._szelesseg

    @property
    def hossz(self):
        """Hossz lekérdezése"""
        return self._hossz

    @hossz.setter
    def hossz(self, ertek):
        """Hossz beállítása ellenőrzéssel"""
        if ertek > 0:
            self._hossz = ertek
        else:
            raise ValueError("A hossz nem lehet negatív!")

    @property
    def szelesseg(self):
        """Szélesség lekérdezése"""
        return self._szelesseg

    @szelesseg.setter
    def szelesseg(self, ertek):
        """Szélesség beállítása ellenőrzéssel"""
        if ertek > 0:
            self._szelesseg = ertek
        else:
            raise ValueError("A szélesség nem lehet negatív!")

Téglalap tulajdonságainak kezelése

Terület: 15 egység²
Kerület: 16 egység

Öröklődés (Inheritance)

Mi az az öröklődés?

Az öröklődés az objektumorientált programozás egyik alapvető koncepciója. Lehetővé teszi, hogy egy új osztályt hozzunk létre egy meglévő osztály alapján, megörökölve annak tulajdonságait és metódusait.

Képzeld el úgy, mint egy családfát: ahogy a gyerekek öröklik szüleik tulajdonságait, úgy örökli az új osztály (gyerekosztály) a szülőosztály jellemzőit.

Kód újrafelhasználás

Nem kell ugyanazt a kódot többször megírni

Hierarchikus struktúra

Logikus kapcsolat az osztályok között

Bővíthetőség

Könnyű új funkciókat hozzáadni

class Allat:
    """Alap (szülő) osztály"""
    def __init__(self, nev, kor):
        self.nev = nev
        self.kor = kor

    def hang_ad(self):
        return "Valamilyen hangot ad..."

    def bemutatkozik(self):
        return f"{self.nev} vagyok, {self.kor} éves."


class Kutya(Allat):
    """Származtatott (gyerek) osztály"""
    def __init__(self, nev, kor, fajta):
        # Szülőosztály konstruktorának hívása
        super().__init__(nev, kor)
        # Saját új tulajdonság
        self.fajta = fajta

    def hang_ad(self):
        # Szülőosztály metódusának felülírása
        return "Vau-vau!"

    def porazzal_setalni(self):
        # Saját új metódus
        return f"{self.nev} pórázzal sétál."


class Madar(Allat):
    """Másik származtatott osztály"""
    def __init__(self, nev, kor, szarny_meret):
        super().__init__(nev, kor)
        self.szarny_meret = szarny_meret

    def hang_ad(self):
        return "Csip-csirip!"

    def repul(self):
        return f"{self.nev} repül {self.szarny_meret}cm-es szárnyaival."

Állatok létrehozása

Új kutya

Új madár

Többszörös öröklődés

Pythonban egy osztály több szülőosztályból is örökölhet. Ez azt jelenti, hogy egy osztály több különböző osztály tulajdonságait és metódusait is megörökölheti.

Vigyázat! A többszörös öröklődés bonyolulttá teheti a kódot, ezért csak akkor használjuk, ha valóban szükséges!

class Repulo:
    """Első szülőosztály"""
    def __init__(self):
        self.repul = True

    def felszall(self):
        return "Felszállás..."

    def leszall(self):
        return "Leszállás..."


class Robot:
    """Második szülőosztály"""
    def __init__(self):
        self.elektromos = True

    def toltottseg(self):
        return "100%"

    def tolt(self):
        return "Töltés..."


class DronRobot(Repulo, Robot):
    """Osztály többszörös örökléssel"""
    def __init__(self, nev):
        # Mindkét szülő inicializálása
        Repulo.__init__(self)
        Robot.__init__(self)
        self.nev = nev

    def bemutatkozik(self):
        return f"Én {self.nev} vagyok, egy repülő robot!"

Drón robot létrehozása

Speciális Metódusok

Mik azok a speciális metódusok?

A speciális metódusok (más néven "magic methods" vagy "dunder methods") olyan előre definiált nevű metódusok a Pythonban, amelyek dupla aláhúzással kezdődnek és végződnek (pl. __init__).

Ezek lehetővé teszik, hogy testreszabjuk az osztályok viselkedését különböző Python műveletekhez és beépített függvényekhez.

A leggyakrabban használt speciális metódusok:

  • __init__(self, ...) - Objektum létrehozásakor fut le
  • __str__(self) - Az objektum szöveges megjelenítése (str() függvény)
  • __repr__(self) - Az objektum részletes reprezentációja (programozói nézet)
  • __len__(self) - A len() függvény eredménye az objektumon
  • __add__(self, other) - A + operátor viselkedése
  • __eq__(self, other) - Az == operátor viselkedése
class Pontszam:
    def __init__(self, pont):
        self.pont = pont

    def __str__(self):
        """Felhasználóbarát szöveges megjelenítés"""
        return f"{self.pont} pont"

    def __repr__(self):
        """Programozói részletes megjelenítés"""
        return f"Pontszam(pont={self.pont})"

    def __add__(self, other):
        """+ operátor: két pontszám összege"""
        if isinstance(other, Pontszam):
            return Pontszam(self.pont + other.pont)
        return Pontszam(self.pont + other)

    def __eq__(self, other):
        """== operátor: pontszámok összehasonlítása"""
        if isinstance(other, Pontszam):
            return self.pont == other.pont
        return self.pont == other

# Használat
p1 = Pontszam(10)
p2 = Pontszam(20)
print(p1)          # 10 pont
print(repr(p1))    # Pontszam(pont=10)
print(p1 + p2)     # 30 pont
print(p1 == 10)    # True

Pontszámok kezelése

További gyakori speciális metódusok

Nézzünk egy példát egy olyan osztályra, amely több speciális metódust is használ:

class Kosar:
    def __init__(self):
        self.termekek = []

    def __len__(self):
        """Termékek száma a kosárban"""
        return len(self.termekek)

    def __getitem__(self, index):
        """[] operátor: termék lekérése index alapján"""
        return self.termekek[index]

    def __iter__(self):
        """Kosár bejárhatóvá tétele (for ciklushoz)"""
        return iter(self.termekek)

    def __contains__(self, item):
        """in operátor: termék keresése a kosárban"""
        return item in self.termekek

    def add(self, termek):
        self.termekek.append(termek)

# Használat
kosar = Kosar()
kosar.add("alma")
kosar.add("körte")

print(len(kosar))       # 2
print(kosar[0])        # "alma"
print("alma" in kosar)  # True

for termek in kosar:
    print(termek)

Bevásárlókosár kezelése

Gyakorló Feladatok

1. Feladat: Könyv osztály

Írj egy Könyv osztályt, amely kezeli egy könyv adatait (cím, szerző, oldalszám). A könyv objektumoknak legyen szöveges megjelenítése és legyenek összehasonlíthatók oldalszám alapján.

class Konyv:
    def __init__(self, cim, szerzo, oldalszam):
        self.cim = cim
        self.szerzo = szerzo
        self.oldalszam = oldalszam

    def __str__(self):
        return f"{self.szerzo}: {self.cim} ({self.oldalszam} oldal)"

    def __eq__(self, other):
        return self.oldalszam == other.oldalszam

    def __lt__(self, other):
        return self.oldalszam < other.oldalszam

Próbáld ki!

2. Feladat: Hőmérséklet osztály

Készíts egy Hőmérséklet osztályt, amely képes Celsius és Fahrenheit között átváltani. Használj tulajdonságokat (property) a hőmérséklet lekérdezésére és beállítására.

class Homerseklet:
    def __init__(self, celsius=0):
        self._celsius = celsius

    @property
    def celsius(self):
        return self._celsius

    @celsius.setter
    def celsius(self, ertek):
        self._celsius = ertek

    @property
    def fahrenheit(self):
        return (self._celsius * 9/5) + 32

    @fahrenheit.setter
    def fahrenheit(self, ertek):
        self._celsius = (ertek - 32) * 5/9

Hőmérséklet átváltó

°C
°F

3. Feladat: Bankszámla hierarchia

Hozz létre egy bankszámla osztályhierarchiát: egy alap BankSzamla osztályt, és ebből származtatott MegtakaritasiSzamla és HitelSzamla osztályokat. Mindegyik típusnak legyen saját viselkedése a pénz kivételénél.

class BankSzamla:
    def __init__(self, egyenleg=0):
        self.egyenleg = egyenleg

    def befizet(self, osszeg):
        self.egyenleg += osszeg
        return True

    def kivesz(self, osszeg):
        if osszeg <= self.egyenleg:
            self.egyenleg -= osszeg
            return True
        return False

class MegtakaritasiSzamla(BankSzamla):
    def __init__(self, egyenleg=0, minimum_egyenleg=1000):
        super().__init__(egyenleg)
        self.minimum_egyenleg = minimum_egyenleg

    def kivesz(self, osszeg):
        if self.egyenleg - osszeg >= self.minimum_egyenleg:
            return super().kivesz(osszeg)
        return False

class HitelSzamla(BankSzamla):
    def __init__(self, egyenleg=0, hitelkeret=100000):
        super().__init__(egyenleg)
        self.hitelkeret = hitelkeret

    def kivesz(self, osszeg):
        if self.egyenleg - osszeg >= -self.hitelkeret:
            self.egyenleg -= osszeg
            return True
        return False

Bankszámlák kezelése