# 9. hét: osztályok

Czirkos Zoltán, Frey Balázs · 2025.09.06.

Osztályok definíciója, példányosítása. Paraméterátadás.

## 1. Összetett adatszerkezet

Az itt látható táblázat egy futóverseny eredményeit tartalmazza.

Index | Név | Születés | Helyezés
--- | --- | --- | ---
0 | Am Erika | 1994. 05. 06. | 1
1 | Break Elek | 2001. 09. 30. | 3
2 | Dil Emma | 1998. 08. 25. | 2
3 | Kasza Blanka | 1989. 06. 10. | 5
4 | Reset Elek | 2001. 04. 05. | 4

Alább egy elkezdett programot látsz, amelyben a megfelelő típusok már definiálva vannak, és az adatokat egy lista tartalmazza. Egészítsd ki a programot, hogy kiírja a képernyőre a kommentekben megadott adatokat!

    class Datum:
        def __init__(self, ev, honap, nap):
            self.ev = ev
            self.honap = honap
            self.nap = nap

    class Versenyzo:
        def __init__(self, nev, szuletes, helyezes):
            self.nev = nev
            self.szuletes = szuletes
            self.helyezes = helyezes

    def datum_str(d):
        pass    # később kiegészítendő

    def versenyzo_str(v):
        pass    # később kiegészítendő

    def main():
        versenyzok = [
            Versenyzo("Am Erika", Datum(1994, 5, 6), 1),
            Versenyzo("Break Elek", Datum(2001, 9, 30), 3),
            Versenyzo("Dil Emma", Datum(1998, 8, 25), 2),
            Versenyzo("Kasza Blanka", Datum(1989, 6, 10), 5),
            Versenyzo("Reset Elek", Datum(2001, 4, 5), 4),
        ];

        # 0-s versenyző neve
        # 2-es versenyző helyezése
        # 4-es versenyző születési dátuma (írd meg a datum_str függvényt!)
        # 1-es versenyző nevének kezdőbetűje
        # az 1-es versenyző dobogós-e? igen/nem
        # az 4-es versenyző gyorsabb-e, mint a 3-as versenyző?
        # az 1-es versenyző ugyanabban az évben született-e, mint a 2-es?
        # egészítsd ki a versenyzo_str() függvényt, és írd ki az 1-es versenyző adatait
        # végül listázd ki az összes versenyzőt sorszámozva, összes adatukkal.

    main()

Megoldás

    class Datum:
        def __init__(self, ev, honap, nap):
            self.ev = ev
            self.honap = honap
            self.nap = nap

    class Versenyzo:
        def __init__(self, nev, szuletes, helyezes):
            self.nev = nev
            self.szuletes = szuletes
            self.helyezes = helyezes

    def datum_str(d):
        return f"{d.ev}. {d.honap:02}. {d.nap:02}."

    def versenyzo_str(v):
        return f"Nev: {v.nev}\nSzületési dátum: {datum_str(v.szuletes)}\nHelyezés: {v.helyezes}\n"

    def main():
        versenyzok = [
            Versenyzo("Am Erika", Datum(1994, 5, 6), 1),
            Versenyzo("Break Elek", Datum(2001, 9, 30), 3),
            Versenyzo("Dil Emma", Datum(1998, 8, 25), 2),
            Versenyzo("Kasza Blanka", Datum(1989, 6, 10), 5),
            Versenyzo("Reset Elek", Datum(2001, 4, 5), 4),
        ];

        # 0-s versenyző neve
        print("0. versenyző neve:", versenyzok[0].nev)

        # 2-es versenyző helyezése
        print("2. versenyző helyezése:", versenyzok[2].helyezes)

        # 4-es versenyző születési dátuma (írd meg a datum_str függvényt!)
        print("4. versenyző születési dátuma:", datum_str(versenyzok[4].szuletes))

        # 1-es versenyző nevének kezdőbetűje
        print("1. versenyző nevének kezdőbetûje:", versenyzok[1].nev[0])

        # az 1-es versenyző dobogós-e? igen/nem
        if versenyzok[1].helyezes <= 3:
            print("Az 1. versenyző dobogós lett")
        else:
            print("Az 1. versenyző nem lett dobogós")

        # az 4-es versenyző gyorsabb-e, mint a 3-as versenyző?
        if versenyzok[4].helyezes < versenyzok[3].helyezes:
            print("A 4. versenyző gyorsbb volt, mint a 3. versenyző")
        else:
            print("A 4. versenyző nem volt gyorsabb, mint a 3. versenyző")

        # az 1-es versenyző ugyanabban az évben született-e, mint a 2-es?
        if versenyzok[1].szuletes.ev == versenyzok[2].szuletes.ev:
            print("Az 1. versenyző ugyanabban az évben született, mint a 2. versenyző")
        else:
            print("Az 1. versenyző más évben született, mint a 2. versenyző")

        # egészítsd ki a versenyzo_str() függvényt, és írd ki az 1-es versenyző adatait
        print("Az 1. versenyző adatai:", versenyzo_str(versenyzok[1]))

        # végül listázd ki az összes versenyzőt sorszámozva, összes adatukkal.
        print("A versenyzők adatai:")
        for i in range(len(versenyzok)):
            print(f"A(z) {i}. versenyző adatai:\n" + versenyzo_str(versenyzok[i]))

    main()

Korábbi előadáson már láttuk, hogy a `for()` ciklusban a ciklusváltozó nem az indexeket vesz fel értékül, hanem tényleges elemeket. Ha mégis szükségünk lenne az indexre, `while()` ciklust használtunk, vagy a mintamegoldásban használt `range(len(lista))` trükköt. Van azonban egy másik megoldás, ahol két ciklus változónk van, az első az indexet, a második az elem értékét (tehát `lista[i]`-t) veszi fel:

    for i, v in enumerate(versenyzok): #i==0 v==versenyzok[0]
        print(f"A(z) {i}. versenyző adatai:", versenyzo_str(v))

Erről később még lesz szó előadáson.

## 2. Az __str__(self) függvény

Az előadáson szerepelt, hogy az osztály belsejében definiált `__str__` függvénnyel „meg lehet tanítani” a Pythonnak, hogy egy adott típus hogyan konvertálható sztringgé.

Módosítsd az előző programot úgy, hogy a `datum_str()` és a `versenyzo_str()` függvények helyett `__str__` függvényeket kapnak az osztályok! Ezek után pedig, írd át a főprogramot, ahol kell.

Megoldás

    class Datum:
        def __init__(self, ev, honap, nap):
            self.ev = ev
            self.honap = honap
            self.nap = nap

        def __str__(self):
            return f"{self.ev}. {self.honap:02}. {self.nap:02}."

    class Versenyzo:
        def __init__(self, nev, szuletes, helyezes):
            self.nev = nev
            self.szuletes = szuletes
            self.helyezes = helyezes

        def __str__(self):
            return f"Nev: {self.nev}\nSzületési dátum: {self.szuletes}\nHelyezés: {self.helyezes}\n"

    def main():
        versenyzok = [
            Versenyzo("Am Erika", Datum(1994, 5, 6), 1),
            Versenyzo("Break Elek", Datum(2001, 9, 30), 3),
            Versenyzo("Dil Emma", Datum(1998, 8, 25), 2),
            Versenyzo("Kasza Blanka", Datum(1989, 6, 10), 5),
            Versenyzo("Reset Elek", Datum(2001, 4, 5), 4),
        ];

        # 0-s versenyző neve
        print("0. versenyző neve:", versenyzok[0].nev)

        # 2-es versenyző helyezése
        print("2. versenyző helyezése:", versenyzok[2].helyezes)

        # 4-es versenyző születési dátuma
        print("4. versenyző születési dátuma:", versenyzok[4].szuletes)

        # 1-es versenyző nevének kezdőbetűje
        print("1. versenyző nevének kezdőbetûje:", versenyzok[1].nev[0])

        # az 1-es versenyző dobogós-e? igen/nem
        if versenyzok[1].helyezes <= 3:
            print("Az 1. versenyző dobogós lett")
        else:
            print("Az 1. versenyző nem lett dobogós")

        # az 4-es versenyző gyorsabb-e, mint a 3-as versenyző?
        if versenyzok[4].helyezes < versenyzok[3].helyezes:
            print("A 4. versenyző gyorsbb volt, mint a 3. versenyző")
        else:
            print("A 4. versenyző nem volt gyorsabb, mint a 3. versenyző")

        # az 1-es versenyző ugyanabban az évben született-e, mint a 2-es?
        if versenyzok[1].szuletes.ev == versenyzok[2].szuletes.ev:
            print("Az 1. versenyző ugyanabban az évben született, mint a 2. versenyző")
        else:
            print("Az 1. versenyző más évben született, mint a 2. versenyző")

        # írd ki az 1-es versenyző adatait
        print("Az 1. versenyző adatai:", versenyzok[1])

        # végül listázd ki az összes versenyzőt sorszámozva, összes adatukkal.
        print("A versenyzők adatai:")
        for i in range(len(versenyzok)):
            print(f"A(z) {i}. versenyző adatai:\n{versenyzok[i]}")

    main()

## 3. Időpontok

Írj programot, amely egy osztályban időpontot tárol: óra, perc. Írjunk függvényeket ehhez:

- `ido_kiir(i)`: kiírja az időpontot óra:perc formában.
- `ido_hozzaad(i, p)`: hozzáad `p` percet az `i` időponthoz, és visszatér az új időponttal. Pl. 15:15 + 45 = 16:00.
- `ido_eltelt(i1, i2)`: megmondja, hány perc telt el a két időpont között, pl. 16:30-15:15 = 75 perc. (A paraméterek sorrendje a kivonásnál megszokott: kisebbítendő, kivonandó.)
- `ido_kivon(i, p)`: kivon `p` percet az `i` időpontból, és visszatér az új időponttal. Pl. 15:45 - 30 = 15:15.

Tipp

A feladat megoldása közben gyakran kell 60 perces átváltást csinálni, pl. 15:15 + 70 = 15:85 = 16:25. Érdemes olyan konstruktort írni az osztálynak, amely órát és percet vesz át paraméterként, és ezt az átváltást megcsinálja – ahogy az előadáson is szerepelt a tört osztály és az egyszerűsítés kapcsán. Akkor nem kell majd több helyen is foglalkozni ezzel, duplikálni a kódot.

Megoldás

A 60 perc és a 24 óra kezelésére a percek hozzáadásánál nagyon jól használható a maradékképzés. Pl. `16:55+10` esetén: `:55+10 = :65`, ami helyett a következő óra `:05` kellene. `65%60`, vagyis a 60-nal osztás maradéka pont a percet adja, `65/60`, maga az osztás pedig 1-et, amennyivel az órát meg kell növelni. Az órát utána egyszerűen 24-gyel modulózzuk, mert a napokkal már nem kell foglalkozni.

A kivonást ugyanígy lehet kezelni.

    class Idopont:
        def __init__(self, ora, perc):
            self.ora = ora
            self.perc = perc

    def ido_kiir(i):
        print(f"{i.ora:2}:{i.perc:02}")

    def ido_hozzaad(i, p):
        novelt = i.perc+p
        i.perc = novelt%60
        i.ora += novelt//60
        i.ora %= 24
        return i

    def ido_eltelt(i1, i2):
        return (i1.ora - i2.ora)*60 + (i1.perc - i2.perc)

    def ido_kivon(i, p):
        csokkentett = i.perc-p
        i.perc = csokkentett%60
        i.ora += csokkentett//60
        i.ora %= 24
        return i

    def main():
        i = Idopont(22, 2)
        ido_kiir(i) # 22:02
        i = ido_hozzaad(i, 40)
        ido_kiir(i) # 22:42
        i = ido_hozzaad(i, 20)
        ido_kiir(i) # 23:02
        i = ido_hozzaad(i, 60)
        ido_kiir(i) #  0:02

        print(ido_eltelt(Idopont(20, 00), Idopont(14, 30)))

        i = Idopont(0, 2)
        ido_kiir(i) #  0:02
        i = ido_kivon(i, 60)
        ido_kiir(i) # 23:02
        i = ido_kivon(i, 20)
        ido_kiir(i) # 22:42
        i = ido_kivon(i, 40)
        ido_kiir(i) # 22:02

    main()

## 4. Kerítés

Az előadásban szerepelt a pont típus. Ebben a feladatban ezzel kell megoldanod egy problémát.

Add meg a `Pont` típust, amely kétdimenziós koordinátát (`x`, `y`) tárol! Írj ehhez függvényeket:

- `tav()`: a paraméterként kapott két pont távolságával tér vissza! (Ehhez Pitagorasz tételét kell használni.)
- `egyenlo()`: megvizsgál két pontot, és megmondja, hogy egybeesnek-e.
- `beolvas()`: beolvassa egy pont koordinátáit a billentyűzetről, és visszatér vele.

Ha ezek megvannak, az eddigiek használatával oldd meg az alábbi feladatot:

### Kerítés hossza

Egy gazda szeretné körbekeríteni a telkét drótkerítéssel. Írj programot, amely kiszámítja, hogy mennyi kerítésre lesz szüksége! A program kérje egymás után a kerítésoszlopok koordinátáit (x, y koordinátapárok), számítsa ki az aktuális és az előző oszlop távolságát, és összegezze a távolságokat! Az összegzést addig folytassa, amíg a megadott koordináták nem egyeznek az elsőként megadott koordinátapárral, vagyis míg vissza nem ér a kezdőoszlophoz!

Ehhez célszerű egy változóba följegyezni a kezdőpontot, azután pedig két további, pont típusú változóval dolgozni: az egyik tárolja az új pont adatait, a másik pedig mindig az eggyel előzőt.

Megoldás

A megoldáshoz nem kell listát használni: mindig csak az előző koordinátára kell emlékezni, és azzal az új kerítésszakasz hossza kiszámítható. Hasonlóképp, az első oszlop koordinátáit eltárolva észre tudja venni a program, amikor a legutóbbi beírt pont egybeesik az elsővel.

    import math

    class Pont:
        def __init__(self, x, y):
            self.x = x
            self.y = y

    def tav(p1, p2):
        return math.sqrt((p1.x-p2.x)**2 + (p1.y-p2.y)**2)

    def egyenlo(p1, p2):
        return p1.x==p2.x and p1.y==p2.y

    def beolvas():
        x = float(input("X koordináta: "))
        y = float(input("Y koordináta: "))
        return Pont(x,y)

    def main():
        elso = elozo = beolvas()
        kovetkezo = beolvas()
        hossz = 0
        while not egyenlo(kovetkezo, elso):
            hossz += tav(kovetkezo, elozo)
            elozo = kovetkezo
            kovetkezo = beolvas()
        hossz += tav(kovetkezo, elozo)

        print("A kerítés hossza:", hossz)

    main()

## 5. Szállóvendégek

Egy hétemeletes szállodában a szobafoglalásokat listában tárolják. A szobák a szokásos módon vannak számozva, a százasok adják meg az emeletet, a többi pedig a szoba sorszámát (pl. 712 = 7. emelet, 12. szoba). A földszint a 0. szint, utána 1-től 7-ig az emeletek. Ennél a feladatnál nem kell teljes programot írni, csak a megadott részeket.

- Definiálj `Vendeg` nevű típust, amelyik egy szállóvendég adatait (név: sztring, szobaszám: egész) tartalmazza! Írj függvényt, amely átvesz egy vendéget, és visszaadja, hogy melyik emeleten lakik!
- Írj függvényt, amely átvesz egy `Vendeg` elemekből álló listát és egy nevet! Keresse ez meg a névhez tartozó foglalást és adja vissza a megtalált listaelemet vagy `None`-t, ha nincs találat!
- Írj függvényt, amely paraméterként kapja a vendégek listáját és visszaad egy másik listát, amelyet a szint sorszámával indexelünk! Írja be az utóbbi listába, hogy az egyes emeleteken hány vendég lakik! Ha van üres emelet, annak is szerepelnie kell ebben.
- Írj függvényt, amely megkapja a vendégek listáját, az előző függvénnyel előállítja a betöltöttségek listáját, és végül visszatér a legzsúfoltabb emelet sorszámával – tehát azzal, ahol a legtöbb vendég van éppen!
- Írj főprogramot, amelyben létrehozod a listát az alábbi foglalásokkal, és megkeresed a legzsúfoltabb emeletet!

Név | Szoba
--- | ---
Dia Dóra | 712
Elektro M Ágnes | 713
Érték Elek | 506

Megoldás

    class Vendeg:
        def __init__(self, nev, szobaszam):
            self.nev = nev
            self.szobaszam = szobaszam

    # v: Vendeg
    # vissza: int
    def hanyadik_emeleten(v):
        return v.szobaszam // 100

    # foglalasok: [Vendeg, Vendeg, Vendeg, ...]
    # nev: "Minta Áron"
    # vissza: Vendeg
    def vendeg_keres(foglalasok, nev):
        for i in range(0, len(foglalasok)):
            if nev == foglalasok[i].nev:
                return foglalasok[i]
        return None

        # Ez is teljesen jó megoldás
        # for v in foglalasok:
        #     if nev == v.nev:
        #         return v
        # return None

    # foglalasok: [Vendeg, Vendeg, Vendeg, ...]
    # vissza: [0, 1, 12, 7, 0, 5, 8, 3]
    def melyik_emeleten_hanyan(foglalasok):
        hanyan = [0] * 8
        for i in range(len(foglalasok)):
            v = foglalasok[i]           # Vendeg
            e = hanyadik_emeleten(v)    # int
            hanyan[e] += 1
        return hanyan

        # Ez is teljesen jó megoldás
        # hanyan = [0] * 8
        # for v in foglalasok:
        #     hanyan[hanyadik_emeleten(v)] += 1
        # return hanyan

    # foglalasok: [Vendeg, Vendeg, ...]
    # vissza: int
    def legzsufoltabb(foglalasok):
        hanyan = melyik_emeleten_hanyan(foglalasok) # [0, 1, 12, 7, ...]
        maxidx = 0
        for i in range(1, len(hanyan)):
            if hanyan[i] > hanyan[maxidx]:
                maxidx = i
        return maxidx

    # Ezt nem kérte a feladat, csak a teszt kedvéért
    def main():
        foglalasok = [
            Vendeg("Dia Dóra", 712),
            Vendeg("Elektro M Ágnes", 713),
            Vendeg("Érték Elek", 506),
        ]
        v = vendeg_keres(foglalasok, "Dia Dóra")
        print("Vendég szobája:", v.szobaszam)
        print("Hányadikon:", hanyadik_emeleten(v))

        print("Betöltöttség:", melyik_emeleten_hanyan(foglalasok))
        print("Legzsúfoltabb:", legzsufoltabb(foglalasok))

    main()

## 6. Charlie

Charlie fagylaltot árul: jelenleg pisztácia, vanília, tutti-frutti, karamell, rumos dió és kávé a választék, de lehetne többféle is. A fagyit íz alapján kérhetik a gyerekek, egyszerre csak egy gombócot. A készlet véges, minden gombóc eladásával értelemszerűen eggyel csökken.

Definiálj osztályt, ami egy fagyi adatait (íz, hány gombóc van) tárolja! Írj függvényt, amely a fagyi objektumok listáját kapja és egy ízt; vissza pedig a megtalált elem referenciáját adja, vagy `None`-t!

Egészítsd ki ezt teljes programmá, amely a vásárlásokat kezeli! Hozz létre egy fagyi listát, és töltsd fel adatokkal. Olvasd be a vásárlásokat (ízeket) fájl vége jelig. Keresd meg az előbb megírt függvénnyel a kapott ízt, és jelezd a vásárlás eredményét: sikeres, kifogyott (volt, de 0-ra csökkent), nem is volt!

Íz | Mennyiség
--- | ---
pisztácia | 0
vanília | 3
tutti-frutti | 8
karamell | 4
rumos dió | 5
kávé | 9