Kihagyás

5. gyakorlat

A gyakorlat anyaga

Ezen a gyakorlaton szó lesz a Python nyelven történő kivételkezelésről. Ezt követően áttekintjük a fájlkezelés alapjait is, amelynek során megismerkedünk a kontextuskezelő mechanizmussal.

Kivételkezelés

A kivétel fogalma

A kód futása során bekövetkezhet olyan nem várt esemény, amely meggátolja a program futását az adott blokkban. Pythonban ebben az esetben úgynevezett kivétel dobódik. A kivétel lényegében egy beépített Python objektum, amely egy hibát reprezentál.

Példa: A nullával való osztás ZeroDivisionError típusú kivételt eredményez

1
2
valami = 5 / 0
print("Angry matematikus noises...")

Kimenet

Traceback (most recent call last): File "main.py", line 1, in <module> valami = 5 / 0 ZeroDivisionError: division by zero

A Pythonban számos beépített kivételosztály létezik. Ezek a kivételosztályok egy öröklődési hierarchiát alkotnak, amit az alábbi ábrán láthatunk.

Kivétel dobása

Pythonban a raise utasítás segítségével mi is dobhatunk manuálisan egy kivétel objektumot. A raise kulcsszó után meghívjuk annak a kivételosztálynak a konstruktorát, amilyen típusú kivételt el szeretnénk dobni.

1
raise Exception()   # Exception típusú kivétel dobása

A beépített kivételeknek kétféle konstruktoruk van: egy default (paraméter nélküli) és egy, ami egy szöveget vár paraméterül ("hibaüzenet"). Az utóbbi főként akkor lehet hasznos, ha a szöveges paramétert később fel szeretnénk használni.

1
raise Exception("Valamit elszúrtunk...")

Pythonban (Javával ellentétben) sehol sem kell jelezni a kivétel dobását.

Kivételek elkapása

Ha valahol el lett dobva egy kivétel, akkor azt el is tudjuk kapni.

Pythonban a try, except és finally utasításokat használjuk a kivételkezelésre. Ezek ugyanúgy működnek, mint Javában a try, catch és finally kulcsszavak.

1
2
3
4
5
6
7
8
try:
    # a kód azon része, ahol kivétel dobódhat
except ExceptionType as e:
    # ExceptionType típusú kivétel elkapása (e-ként hivatkozhatunk rá)
except Exception:
    # Exception típusú kivétel elkapása 
finally:
    # mindenképpen lefutó kódrész

Több except ág esetén a legelső, a dobott kivétel típusra illeszkedő fut le.

Példák

Példa: Írjunk egy osztás függvényt, amely két egész szám paramétert vár (a és b), visszatérési értéke pedig az a/b hányados! Ha nem egész típusú paramétereket kapunk vagy ha nullával szeretnénk osztani, dobjunk kivételt!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
def osztas(a, b):
    if isinstance(a, int) and isinstance(b, int):
        if b == 0:
            raise ZeroDivisionError("Ejnye, nullával nem osztunk!")
        return a / b

    raise TypeError("Egész paramétereket adj meg!")

# === kivételkezelés ===

try:
    print(osztas(5, 2))
    print(osztas(5, 0))     # ZeroDivisionError!
    print(osztas(3, 2))
except ZeroDivisionError as zde:
    print(zde)
except TypeError as te:
    print(te)
except Exception:
    print("Valami egyéb hiba történt...")
finally:
    print("--- kivételkezelés vége ---")

Kimenet

2.5 Ejnye, nullával nem osztunk! --- kivételkezelés vége ---

Példa: A Sutemeny osztály, amelynek konstruktorában kivételt dobunk, ha a szeletek paraméter értéke negatív.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class Sutemeny(object):
    def __init__(self, nev, szeletek_szama):
        if szeletek_szama < 0:
            raise Exception("HIBA: Negatív szeletszám!")
        else:
            self._nev = nev
            self._szeletek = szeletek_szama

            print(self._szeletek, "szeletes", self._nev, "létrehozva.")

    # ...

# === kivételkezelés ===

try:
    suti1 = Sutemeny("krémes", 12)
    suti2 = Sutemeny("brownie", 0)
    suti3 = Sutemeny("tiramisu", -8)    # Exception!
except Exception as e:
    print(e)

Kimenet

12 szeletes krémes létrehozva. 0 szeletes brownie létrehozva. HIBA: Negatív szeletszám!

Saját kivételosztály

Ha szeretnénk, akkor Pythonban akár saját kivételosztályt is írhatunk. Ezt valamely létező kivételosztályból kell származtatnunk (például az Exception ősosztályból).

Példa: Saját kivételosztály írása

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class NincsPisztaciaException(Exception):
    def __init__(self, uzenet):
        self.uzenet = uzenet

try:
    fagyi_izek = ["csoki", "vanília", "szamóca", "málna", "karamell"]
    if "pisztácia" not in fagyi_izek:
        raise NincsPisztaciaException("Elfogyott a pisztácia!")
except NincsPisztaciaException as npe:
    print(npe)

Kimenet

Elfogyott a pisztácia!

Fájlkezelés

A Python lehetőséget biztosít különböző fájlműveletek (például fájl megnyitása, fájlból olvasás, fájlba írás stb.) elvégzésére. Az alábbiakban áttekintjük a Python legfontosabb fájlkezeléssel kapcsolatos beépített függvényeit.

Fájlkezelő függvények

  • open(filename, mode): fájl megnyitása
    • filename: a megnyitni kívánt fájl neve (szöveg)
    • mode: a fájlmegnyitás módja (szöveg)
      • "r": olvasásra (alapértelmezett)
      • "w": írásra (felülírja a fájl korábbi tartalmát!)
      • "a": fájl végéhez való hozzáfűzésre
      • "x": kizárólagos létrehozásra (ha már létezik a fájl, akkor hibát kapunk)
      • egy fájlt megnyithatunk szöveges ("t") vagy bináris ("b") módban - ezek közül a szöveges mód az alapértelmezett, mi is mindig ezt fogjuk használni
  • read(n): n darab karakter beolvasása (ahol éppen vagyunk a fájlban)
  • readline(): egy sor beolvasása (ahol éppen vagyunk a fájlban)
  • readlines(): az egész fájl beolvasása, soronként
  • write(szoveg): adott szöveg írása fájlba
  • close(): a megnyitott fájl lezárása
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# a be.txt fájl megnyitása olvasásra
file = open("be.txt", "r")      

# a teljes fájl tartalmának beolvasása
tartalom = file.readlines()

# a tartalom kiíratása soronként
for sor in tartalom:
    sor = sor.rstrip()      # sorvége-jel eltávolítása
    print(sor)

# a megnyitott fájl lezárása
file.close()                    

Kontextuskezelő használata

A fájlkezelés során sajnos mindenféle kivételek keletkezhetnek. Ekkor nem záródik le a megnyitott fájl ("beragadt fájlok").

A kivételek kezelésére használhatjuk a fentebb tanultakat.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
try:
    file = open("be.txt", "r")
    tartalom = file.readlines()

    for sor in tartalom:
        sor = sor.rstrip()
        print(sor)

    file.close()
except FileNotFoundError as fnfe:
    print("A fájl nem található!")

Felmerül viszont a kérdés, hogy ezek segítségével lekezeljük-e az összes lehetséges hibát, ami a fájlkezelés során adódhat?

Pythonban ha biztonságossá akarjuk tenni a fájlkezelést, akkor használjuk az úgynevezett kontextuskezelő mechanizmust a fájlkezeléskor.

A kontextuskezelő (context manager) gondoskodik a megnyitni kívánt erőforrás megfelelő megnyitásáról és lezárásáról - még akkor is, ha a fájlkezelés során kivétel keletkezik. A kontextuskezelő segítségével elkerülhetők a megnyitva maradt, "beragadt" fájlok.

Pythonban a kontextuskezelő mechanizmust a with kulcsszóval tudjuk használni.

Egy fájl megnyitásának elfogadott módja, kontextuskezelő használatával:

1
2
3
4
with open("be.txt", "r") as my_file:
    # a megnyitott fájlra my_file-ként hivatkozhatunk

    tartalom = my_file.readline()

Példa

Példa: A be.txt minden sora egy-egy egész számot tartalmaz. Olvassuk be a fájl tartalmát, majd számítsuk ki a fájlban szereplő értékek átlagát! Ezt írjuk ki egy ki.txt nevű állományba!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
atlag = 0                       # változó az átlagnak

with open("be.txt", "r") as f:  # be.txt megnyitása olvasásra
    osszeg = 0                  # a beolvasott számok összege (az átlaghoz kell)
    darab = 0                   # hány számot olvastunk be (az átlaghoz kell)

    sor = f.readline()          # első sor beolvasása

    while sor:                  # amíg van sor a fájlban...
        osszeg += int(sor)      # ...hozzáadjuk az adott számot az összeghez
        darab += 1              # ...növeljük a beolvasott számok darabszámát
        sor = f.readline()      # ...beolvassuk a következő sort

atlag = osszeg / darab          # az átlag az összeg és a darabszám hányadosa

with open("ki.txt", "w") as f:  # ki.txt megnyitása írásra
    f.write("Az átlag: " + str(atlag) + "\n")  # az eredmény fájlba írása

Feladatok

Az anyagrészhez tartozó gyakorló feladatsor elérhető itt.


Utolsó frissítés: 2020-10-11 18:24:31