Tartalomjegyzék

< Python

Python Objektum Orientált Programozás

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:

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

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:

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.

Típusok

A 3.5-ös Python verziótól használható:

main.py
class Employee:
  name: str
  city: str
  salary: int
 
  def __init__(self, name, city, salary):
    self.name = name
    self.city = city
    self.salary = salary
 
mari = Employee('Mari', 'Pécs', 395)
 
print(mari.name, "fizetése:", mari.salary, "LTC")
main.py
class Employee:
  name: str
  city: str
  salary: int
 
  def __init__(self, name, city, salary):
    self.name = name
    self.city = city
    self.salary = salary
 
  def __str__(self):
    return f"{self.name} ({self.city} Fizetés: {self.salary} LTC)"
 
mari = Employee('Mari', 'Pécs', 395)
 
print(mari)
 
# Kimenet: Mari (Pécs Fizetés: 395 LTC)

Külső linkek

1)
Az is operátor megmondja, hogy két különböző nevű szimbólum azonos objektumra mutat vagy nem.
2)
A Python nyelvben minden objektumnak van egy azonosítója. Az id() függvény segítségével lekérdezhető ez az azonosító.