Tartalomjegyzék
Python Objektum Orientált Programozás
- Szerző: Sallai András
- Copyright © Sallai András, 2011, 2020, 2022
- Web: https://szit.hu
Osztály
Az osztályban első sorban az összetartozó tulajdonságokat tároljuk.
A következő példában tároljuk el egy dolgozó, nevét, a települését, és életkorát. A feladat a következő UML diagrammal ábrázolható.
Az UML diagramban az osztályt egy téglalappal ábrázoljuk, amelyet vízszintesen 3 részre osztunk. A legfelső részbe kerül az osztály neve, középen a tulajdonságok, és alul a viselkedések. Jelenleg egyetlen viselkedés sincs.
Az osztályban tárol tulajdonságokat adattagoknak is szokás hívni.
A Python nyelvben az osztályt a class kulcsszóval vezetjük be. A class után az osztály neve következik, amit egy kettőspont (:) követ.
Üres osztályra példa:
class Dolgozo: pass
A pass azt jelenti, hogy nem valósítottuk meg az osztály, majd később fogjuk megtenni.
A Dolgozo osztályra példa:
- dolgozo.py
class Dolgozo: nev = 'Névtelen' telepules = 'Ismertelen' kor = 0 janos = Dolgozo() janos.nev = 'Nagy János' janos.telepules = 'Szolnok' janos.kor = 35 print('{}\n{}\n{}'.format( janos.nev, janos.telepules, janos.kor ))
A fenti példában az osztály neve után : (kettőspont) áll. Zárójelek is szerepelhetenk a kettőspont előtt:
class Dolgozo(): pass
A zárójeleknek a később tárgyalt öröklésnél lesz szerepe, de így öröklés nélkül is kitehetők.
Mutató az osztályra
Tévesen szokás példányosításnak megadni:
class Dolgozo: pass pali = Dolgozo
Vegyük észre a zárójelek hiányát. Ilyen esetben nem történik konstruktor hívás, és nem jön létre új példány! Csak egy újabb mutató jön létre a Dolgozo osztályra. Ha most létrehozok a pali számára egy nev adattagot, az létrejön a Dolgozo osztály számára is:
class Dolgozo: pass pali = Dolgozo pali.nev = 'Nagy Pál' print(Dolgozo.nev)
A Dolgozo néven keresztül megkapjuk a nev mező tartalmát. Ha a pali egy objektum példány lenne, ez nem történhetne meg.
De nézzük meg az „is” 1) operátorral:
class Dolgozo: pass pali = Dolgozo print(Dolgozo is pali)
Az eredmény True lesz, vagyis annyit csináltunk, hogy az Dolgozo osztály két néven érhető el. De akkor logikusabb lenne egy ilyen:
class Dolgozo: pass Munkas = Dolgozo
De megnézhetjük az objektumok azonosítóját is, az id() 2) függvénnyel:
class Dolgozo: pass pali = Dolgozo print(id(Dolgozo)) print(id(pali))
Eredményül ugyanazt kapjuk, a címük megegyezik. Ebből következik, hogy példány így nem hozható létre. Ha statikusan használjuk a Dolgozo osztályt, akkor használhatjuk zárójelek nélkül, az osztály nevét, de akkor nem szoktunk mutatót létrehozni a már meglévő osztályra. A következő példa helyes, statikus tag használatára:
calss Dolgozo: pass # Beállíthatók egy statikus adattagot: Dolgozo.minimumFizetes = 348
Vegyük észre, hogy nem használtunk zárójelet, a Dolgozo osztály neve után.
Ha pedig példányt akarunk létrehozni, akkor azt helyesen így tehetjük meg:
class Dolgozo: pass pali = Dolgozo()
A pali objektum, így egy példánya a Dolgozo osztálynak.
Metódusok
A metódusok az osztályon belül viselkedést írnak le. Ha egy dolgozót szimulálunk, akkor ilyen viselkedés lehetnek a következők:
- dolgozik
- utazik
- beszél
- pihen
Metódusokkal írjuk le az adattagokat beállító és lekérdező részeket is. A metódusok olyanok mint a függvények, csak osztályon belül.
Az UML ábrán a nev tag beállító és lekérdező metódust látjuk:
class Dolgozo: Nev = "Névtelen" def beallitNev(self, atvettNev): self.Nev = atvettNev def lekerNev(self): return self.Nev Joska = Dolgozo() Joska.beallitNev("Nagy József") print Joska.lekerNev()
Az osztály metódusait a def kulcsszó vezeti be.
Minden metódus első paramétere egy speciális paraméter, mivel ez mindig automatikusan értéket kap, mindig a létrejött objektumra mutat automatikusan. Elnevezése tetszőleges, de szokásosan self.
Konstruktor
A konstruktor az osztály adattagjainak előkészítésére való. Egy speciális metódus, amely egy objektum létrehozásakor kerül meghívásra.
A Python nyelvben a konstruktort egy __init__() nevű metódus jelképezi. Az első paraméter itt is a self.
- kon.py
class Dolgozo: def __init__(self): self.nev = 'Névtelen' self.Kor = 0 self.nem = 'ismeretlen' janos = Dolgozo() janos.nev = 'Nagy János' janos.kor = 35 janos.nem = 'férfi' print(janos.nev) print(janos.kor) print(janos.nem)
A konstruktornak paraméterként átadhatók az adattagok kezdőértékei.
class Dolgozo: def __init__ (self, nev, kor, nem): self.Nev = nev self.Kor = kor self.Nem = nem def lekerKor(self): return self.Kor Joska = Dolgozo("Nagy József", 25, "f") print Joska.Kor
Több alakú konstruktor
class Dolgozo: def __init__ (self, nev='névtelen', kor=0, nem='semleges'): self.nev = nev self.kor = kor self.nem = nem joska = Dolgozo("Nagy József", 25, "f") janos = Dolgozo() lajos = Dolgozo(kor=32) # egyéb példák: piri = Dolgozo() dani = Dolgozo(kor=32, nev="Nagy József)
Öröklés
Az elkészített osztályokat később felhasználhatjuk újabb osztályok létrehozására. Az eredeti osztály ilyenkor a szülő vagy alaposztály lesz. Az osztály örökli az adattagokat és a metódusokat, kivéve a konstruktort.
Az öröklés esetén a szülőosztályt az új osztály neve után adjuk meg. A példánkban az szülőosztály a Dolgozo, amit szeretnénk a Mernok osztályban örökíteni. Ezért így kezdem a Mernok osztály leírását:
class Mernok(Dolgozo):
Egyszerű példa:
class Dolgozo(): def beszel(self): print('Szia') class Mernok(Dolgozo): def pihen(self): print('Alszom...') pali = Mernok() pali.beszel()
Összetettebb példa:
class Dolgozo: def __init__(self): self.Nev="Névtelen" def lekerNev(self): return self.Nev def beallitNev(self, atvettNev): self.Nev = atvettNev class Mernok(Dolgozo): def __init__(self): self.Diploma = "Ismeretlen" def lekerDiploma(self): return self.Diploma # Mérnök példány létrehozása tibi = Mernok() # Az új metódus használata: print(tibi.lekerDiploma()) # Az ősosztály metódusának hívása tibi.beallitNev("Nagy Tibor") # Az ősosztály metódusának hívása print(tibi.lekerNev())
A példában a Mernok osztály örökli a Dolgozo osztály adattagjait és metódusait.
A super() metódus
A super() metódus lehetővé teszi, hogy a szuper osztály eredeti metódusait meghívjuk.
Az ősosztály konstruktorának hívása:
class Dolgozo: def __init__(self): self.Nev="Névtelen" def lekerNev(self): return self.Nev def beallitNev(self, atvettNev): self.Nev = atvettNev class Mernok(Dolgozo): def __init__(self): super().__init__() self.Diploma = "Ismeretlen" def lekerDiploma(self): return self.Diploma # Mérnök példány létrehozása tibi = Mernok() # Az ősosztályban előkészített nev lekérdezése print(tibi.lekerNev())
Ha nem hívjuk meg az ősosztály konstruktorát, akkor nem működik a lekerNev() metódus.
A super() metóduson keresztül, más metódusok hívhatók, a következőben erre látunk egy példát.
Vegyünk egy Dolgozo osztályt, amely a munkát úgy szemlélteti, hogy a képernyőre írja az 'ások' szót. A dolgozó osztályból készítünk egy Mernok osztályt. A munka() metódust, most felülírjuk, ezzel mert a mérnök mér. Ezt úgy látjuk, hogy kiírja a program, hogy 'mérek'. Ha mégis szükség lenne arra, hogy a Mernok osztályból meg tudjuk hívni a Dolgozo osztály munka() metódusát, akkor azt a super() metóduson keresztül tudjuk megtenni:
class Dolgozo: def munka(self): print('ások') class Mernok(Dolgozo): def munka(self): print('mérek') def asas(self): super().munka() joska = Mernok() joska.asas()
Statikus adattagok
Statikus adattag:
class Valami(): EGY=0 KETTO=1 print(Valami.EGY)
A statikus tagok elérhető példányon keresztül is:
valami = Valami() print(valami.EGY)
Más értéket is adhatunk:
valami = Valami() valami.EGY = 3 Valami.EGY = 4
Példány, osztály és statikus metódus
Egy osztályban háromféle metódust hozhatunk létre:
class Mycalss: def my_method(self): return 'példánymetódus hívása', self @classmethod def my_classmethod(cls): return 'osztálymetódus hívása', cls @staticmethod def my_staticmethod(): return 'statikus metódushívás'
Példány metódus
A példánymetódus, egy általános példánymetódus. A példánymetódusok egy önmagukra mutató paramétert igényelnek, de megadhatók más paraméterek is.
A self paraméteren keresztül a példánymetódusok szabadon elérhetik az adattagokat (attribútumok), és más metódusokat, ugyanazon az objektumon. Így könnyen módosítható az objektum állapota.
Osztálymetódus
Az osztálymetódust a classmethod dekorátorral kell megjelölni. A self paraméter helyet az osztálymetódusok egy cls paramétert használnak, amely az osztályra mutat és nem az objektumpéldányra, amikor meghívásra kerül.
Mivel az osztálymetódus csak a cls argumentumhoz fér hozzá, ezért nem módosíthatja az objektum állapotát. Az osztálymetódusok, azonban továbbra is módosíthatják az osztály állapotát, amely az osztály összes példányára érvényes.
Statikusmetódus
A statikusmetódusokat a staticmethod dekorátorral kell megjelölni. Az ilyen metódus nem veszi figyelembe sem a self, sem a cls paramétert, de lehet tetszőleges számú más paramétere.
A statikusmetódus nem módosíthatja sem az objektum, sem az osztály állapotát.
Statikus metódus példa
Saját matematikai osztály:
- math.py
class Math: @staticmethod def abs(x): if x >= 0: return x else: return x * -1 @staticmethod def pow(a, b): if b == 0: return 1 if b == 1: return a tmp = 1 for i in range(b): tmp *= a return tmp print(Math.abs(-9)) print(Math.pow(8, 2))
Kivételkezelés
Finally nélkül
- olvas.py
def tryReadfile(): fp = open('adat.txt', 'r', encoding='utf-8') lista = fp.read().splitlines() fp.close() return lista def readfile(): try: return tryReadfile() except: print('Hiba! A fájl olvasása sikertelen!') print(readfile())
Finally-val
- olvas.py
def tryReadfile(): fp = open('adat.txt', 'r', encoding='utf-8') lista = fp.read().splitlines() datas = [] datas.append(lista) datas.append(fp) return datas def readfile(): try: datas = tryReadfile() lista = datas[0] fp = datas[1] return lista except: print('Hiba! A fájl olvasása sikertelen!') finally: fp.close() print(readfile())
Konstruktor túlterhelése
A Python nyelven a konstruktorból csak egyet írhatok, nincs valódi túlterhelés.
Ha szeretnénk többféle paraméterrel használni, akkor a paramétereknek kezdő értéket kell adni. Így a paraméterek megadása nem kötelező.
- main.py
class Dolgozo: def __init__(self, atvettNev='Névtelen', atvettTelepules='ismeretlen'): self.nev = atvettNev self.telepules = atvettTelepules janos = Dolgozo() print(janos.nev) mari = Dolgozo('Pere Mária', 'Szeged') print(mari.nev)
Konstruktor túlterhelése gyártómetódussal
A konstruktorok túlterhelésére a paraméterek kezdőértékénél tisztább megoldást kínál a gyártómetódus használata.
- gyarto.py
class Dolgozo: def __init__(self, atvettNev: str, atvettTelepules: str): self.nev = atvettNev self.telepules = atvettTelepules @classmethod def gen(cls): return cls(atvettNev='Névtelen', atvettTelepules='ismeretlen') janos = Dolgozo.gen() print(janos.nev) mari = Dolgozo('Pere Mária', 8) print(mari.nev)
Felhasznált webhely:
Metódusok túlterhelése
Ha szeretnék olyan metódust írni, amit többféle képen is lehet paraméterezni adhatunk a paramétereknek kezdőértéket.
- metodus.py
class Dolgozo: def __init__(self, atvettNev, atvettTelepules): self.nev = atvettNev self.telepules = atvettTelepules def beallit(self, atvettNev='Névtelen', atvettTelepules='ismeretlen'): self.nev = atvettNev self.telepules = atvettTelepules mari = Dolgozo('Pere Mária', 'Szeged') print(mari.nev, ' ', mari.telepules) mari.beallit('Park Mária') print(mari.nev, ' ', mari.telepules) mari.beallit('Park Mária', 'Szolnok') print(mari.nev, ' ', mari.telepules)
A multipledispatch modul
Másik megoldás lehet a multipledispatch modul használata. A használathoz a modult telepíteni szükséges:
pip3 install multipledispatch
- dispatch.py
from multipledispatch import dispatch class Dolgozo: def __init__(self, atvettNev, atvettTelepules): self.nev = atvettNev self.telepules = atvettTelepules @dispatch(str) def beallit(self, atvettNev): self.nev = atvettNev @dispatch(str, str) def beallit(self, atvettNev, atvettTelepules): self.nev = atvettNev self.telepules = atvettTelepules mari = Dolgozo('Pere Mária', 'Szeged') print(mari.nev, ' ', mari.telepules) mari.beallit('Park Mária') print(mari.nev, ' ', mari.telepules) mari.beallit('Park Mária', 'Szolnok') print(mari.nev, ' ', mari.telepules)
Interfész
A Python nyelv nem támogatja.
De van néhány modul, amivel hasonló megvalósítható.
- interface
- abc
interface
- main.py
from interface import Interface, implements class iPelda(Interface): def valami(self): pass class Mas(implements(iPelda)) def valami(self): print('Működik')
Láthatóság
Alapértelmezetten minden adattag és metódus látható. A következő láthatóságok állíthatók be:
- public
- protected
- private
A public vagyis nyilvánosan hozzáférésű változók osztályon belül és osztályon kívül is elérhetők.
A protected elérésű változók elérhetők még az ősosztályokban, de azon kívül sehol.
A privát elérésű változók csak az osztályon belül érhetők el.
class Valami: def __init__(self): # public: self.egy = 'első' # protected: self._ketto = 'második' # private self.__harom = 'harmadik'
Láthatjuk, hogy a védelmet az adattag elé írt alsó-vonalak adják.
Gyakori hiba
Ez nem példány:
lajos = Dolgozo
Helyesen:
lajos = Dolgozo()
Nézzük meg a következő program kimenetét:
class Dolgozo: pass lajos = Dolgozo print(lajos is Dolgozo)
Az eredmény True, vagyis a lajos csak egy mutató a Dolgozo osztályra.