Ez az oktatóanyag a Try Except blokk segítségével, programozási példák segítségével magyarázza el a kivételek kezelését Pythonban:
Két hibatípus okozhatja a Python program hirtelen leállását, pl. Szintaxis hibák , és Kivételek Ebben a bemutatóban a második hibatípust (Kivételek) több fontos témakörben tárgyaljuk.
Sokat fogunk profitálni a kivételek kezeléséből az alkalmazásunkban, mint például:
- Robusztus alkalmazás létrehozása.
- Tiszta és hibamentes kód létrehozása.
Python Try Except
Az egyik jó hír, hogy a Python számos beépített kivétellel rendelkezik ahhoz, hogy a kódunkban előforduló hibákat elkapjuk. Emellett lehetőséget ad arra is, hogy saját kivételeket hozzunk létre, ha a beépített kivételek egyike sem felel meg az igényeinknek.
Mi a kivétel
Mi is az a kivétel a Pythonban? Nos, leegyszerűsítve, amikor a Python-interpreter érvénytelen kódot próbál végrehajtani, kivételt vet fel, és azokban az esetekben, amikor egy ilyen kivétel nem kezelhető, megszakítja a program utasításainak normális menetét, és egy visszavezetést ír ki.
Hozzunk létre egy érvénytelen kódot, és nézzük meg, hogyan reagál a Python-értelmező.
Nyissunk egy Python héjat, és futtassuk a következő kódot.
>>>> 50/0
Ez az egyik leggyakoribb hiba a programozásban. A fenti kód megpróbálja elosztani a számot 50 a 0 (nulla). A Python-értelmező ezt érvénytelen műveletnek tekinti és egy ZeroDivisionError , megszakítja a programot, és kinyomtat egy visszakövetési üzenetet.
Világosan láthatjuk, hogy ZeroDivisionError ez a kivétel, ami felmerült. Ez valóban a Python saját módja annak, hogy közölje velünk, hogy nem menő egy számot nullával osztani. Bár más nyelvekben, mint például a JavaScript, ez nem hiba; és a python szigorúan tiltja ezt a gyakorlatot.
Fontos tudni, hogy ez csak egy kivételobjektum, és a Python számos ilyen objektummal rendelkezik. Nézd meg ezt a Python hivatalos dokumentációját, hogy lásd az összes Python beépített kivételt.
Traceback megértése
Mielőtt belemennénk a kivételek kezelésébe, úgy gondolom, hogy segít megérteni, hogy pontosan mi történik, ha a kivételeket nem kezeljük, és hogyan tesz meg mindent a Python, hogy tájékoztasson minket a hibánkról.
Amikor a Python hibával találkozik, akkor kivételt vet fel. Ha ezt a kivételt nem kezeli, akkor valamilyen Traceback nevű információt produkál. Tehát, milyen információt tartalmaz ez a traceback?
Tartalmazza:
- A hibaüzenet, amely megmondja, hogy milyen kivétel lépett fel, és mi történt a kivétel felvetése előtt.
- A hibát okozó kód különböző sorszámai. A hibát okozhatja egy függvényhívásokból álló sorozat, az úgynevezett hívási verem amelyet később tárgyalunk.
Bár ez egy kicsit zavaros, ígérjük, hogy a következő példa több fényt fog vetni a megértésünkre.
Emlékezzünk vissza a fenti 50 és 0 osztásából kinyomtatott visszafejtésre, láthatjuk, hogy a visszafejtés a következő információkat tartalmazza:
- Fájl "": Ez azt mutatja, hogy a kódot konzolterminálról futtatták.
- 1. sor: Ez azt jelenti, hogy a hiba ebben a sorszámban történt.
- ZeroDivisionError: megosztás nulla: Elmondja, hogy milyen kivétel merült fel, és mi okozta azt.
Próbáljunk ki egy másik példát, és talán megnézzük, hogyan lehet egy hívási verem Nyisson meg egy szerkesztőprogramot, írja be az alábbi kódot és mentse el mint tracebackExp .py
def stack1(numb): # 1 div = 0 # 2 stack2(numb, div) # 3 def stack2(numb, div): # 5 compute = numb/div # 6 print(compute) # 7 if __name__ == '__main__': # 9 numb = 5 # 10 stack1(numb) # 11
Nyisson terminált abban a könyvtárban, ahol a fájl található, és futtassa le.
python tracebackExp.py
A következő visszavezetést fogja látni:
A fenti traceback zavarosnak tűnhet, de valójában nem az. A Pythonisták találták ki a traceback olvasásának legjobb módját, ami a alulról felfelé Használjuk tehát ezt a bölcsességet arra, hogy megpróbáljuk megérteni, mit kínál ez a nyomkövetés.
- A legalsó részen a felvetett kivételt és annak okát láthatjuk.
- Felfelé haladva megkapjuk a fájlnevet tracebackExp .py, ahol ez a hiba előfordult, a hibát okozó számítás compute = numb/div, a stack2 függvény, és a 6. sorszámú link, ahol ez a számítás végrehajtásra került.
- Felfelé haladva láthatjuk, hogy a stack2 függvényünket a stack1 függvényben hívtuk meg a 3. sorban.
- A legfelsőre lépve láthatjuk, hogy a 11. sorban a stack1 függvényt hívtuk meg. < modul > azt mondja, hogy a fájl az, amelyik végrehajtásra kerül.
Gyakori Python kivételek
A Python könyvtár rengeteg beépített kivételt definiál. Megnézheti a Python dokumentációban, vagy meghívhatja a beépített helyi () függvényt az alábbiak szerint:
>>> dir(locals()['__builtins__'])
Nem fogunk megpróbálni minden ilyen kivétellel foglalkozni, de megnézünk néhány gyakori kivételt, amelyekkel valószínűleg találkozni fog.
#1) TypeError
Akkor jelenik meg, ha egy műveletet vagy függvényt nem megfelelő típusú objektumra alkalmaznak.
Példa 1
Tekintsük az alábbi programot, amely egy osztót és egy osztót vesz fel, majd kiszámítja és kiírja az osztó és az osztó hányadosának eredményét.
def compute_division(): dividend = int(input("Adja meg az osztót: ")) # cast string to int divisor = input("Adja meg az osztót: ") # nincs casting # osztás kiszámítása result = dividend/divisor # print result print("The result of {}/{} is: {}".format(dividend, divisor, result)) if __name__ == '__main__': result = compute_division()
Az osztó és az osztó értékét kérjük a felhasználótól, de elfelejtjük az osztó string értékét egész számmá alakítani. Így az osztó típusa integer( int ) és az osztó típusa string( str ). Ezután megkapjuk a TypeError mivel az osztás operátor (/) nem működik a karakterláncokon.
Talán érdekes lehet tudni, hogy a Pythontól eltérően a Javascriptben van Type Coercion, amely alapvetően az egyik operandus típusát a másik operandus típusának megfelelő értékké alakítja, ha az operandusok különböző típusúak.
#2) ValueError
Ez akkor jelenik meg, ha egy művelet vagy függvény olyan argumentumot kap, amelynek típusa megfelelő, de értéke nem megfelelő.
Példa 2
Tekintsük a programunkat a Példa 1 fent.
Ha a felhasználó egy alfanumerikus értéket ad be az osztalékhoz, mint például '3a', akkor a programunk ValueError kivételt fog generálni. Ennek az az oka, hogy bár a Python int() metódus bármilyen számot vagy karakterláncot elfogad és egész szám objektumot ad vissza, a karakterlánc értéke nem tartalmazhat betűket vagy bármilyen nem numerikus értéket.
#3) AttributeError
Ez a kivétel egy nem létező attribútum hozzárendelésekor vagy hivatkozásakor lép fel.
Példa 3
Tekintsük az alábbi programot, amely egy számot vesz fel, és a Python matematikai modul segítségével kiszámítja a négyzetgyökét.
import math # importáljuk a matematikai könyvtárat, hogy hozzáférjünk a kódjához def compute_square_root(number): # számítsuk ki a négyzetgyökét a matematikai könyvtár segítségével result = math.sqr(number) return result if __name__ == '__main__': # kérjünk bevitelt a számításhoz a felhasználótól number = int(input("Compute Square root of: ")) # hívjuk a függvényt a négyzetgyök kiszámításához.
Amikor a felhasználó beír egy számot, a programunk megpróbál egy függvényt használni a matematikai modulból, hogy kiszámítsa a négyzetgyökét, de itt hibát követtünk el. sqrt helyett tévesen sqr-t írtunk be, ami nem létezik a matematikai modulban.
Tehát megpróbáltunk egy olyan sqr attribútumra hivatkozni, ami nem létezik, és ez az AttributeError kivételhez vezetett. A legtöbben sokszor követünk el ilyen hibát. Tehát nem vagy egyedül.
Kivételek kezelése Try Except segítségével
Programozóként az egyik dolog, amire a legtöbben időt szánnak, hogy robusztus kódot írjunk, ami ellenálló. Olyan kódot, ami nem törik meg néhány hiba miatt. Pythonban ezt úgy érhetjük el, hogy utasításainkat egy próbáld ki a - kivéve nyilatkozat.
Python Try-Except utasítás
A try-except utasítás a következő felépítésű:
try: #a kódod ide kerül except """Specify exception type(s) here"""": #handle exception here
Zárjuk a kódot a tracebackExp .py egy try-except utasításon belül.
def stack1(numb): # 1 div = 0 # 2 stack2(numb, div) # 3 def stack2(numb, div): # 5 try: # 6 compute = numb/div # 7 print(compute) # 8 except ZeroDivisionError as zde: # 9 print(zde) # 10 if __name__ == '__main__': # 12 numb = 5 # 13 stack1(numb) # 14 print("program folyamatos") # 15
A kód futtatása a következő kimenetet eredményezi
Így működik a try-except utasítás. A Python a try blokkban lévő kódot hajtja végre. 7-8. sor Ha nem találunk érvénytelen kódot, akkor a except blokkban lévő kódot kell megadni. 10. sor kihagyásra kerül, és a végrehajtás folytatódik.
De ha érvénytelen kódot találunk, akkor a végrehajtás azonnal megáll a try blokkban, és ellenőrzi, hogy a felvetett kivétel megegyezik-e azzal, amit az except utasításban adtunk meg. 9. sor Ha egyezik, akkor a except blokk végrehajtódik és folytatódik, ha nem, akkor a program megszakad.
A try-blokk általában azt a kódot tartalmazza, amely kivételt okozhat, míg az except-blokk elkapja és kezeli a kivételt.
Többszörös kivételek kezelése Except segítségével
Több kivételt is kezelhetünk egyetlen "except"-el vagy több "except"-el. Minden attól függ, hogyan akarjuk kezelni az egyes kivételeket.
#1) Több kivétel kezelése egyetlen kivétellel
try: #a kódod ide kerül except(Exception1[, Exception2[,...ExceptionN]]]): #a kivétel kezelése itt történik
Ezt a módszert akkor használjuk, ha gyanítjuk, hogy a kódunk különböző kivételeket vethet fel, és minden esetben ugyanazt a műveletet szeretnénk végrehajtani. Tehát ha a Python-értelmező talál egyezést, akkor a except blokkba írt kód fog végrehajtódni.
Nézzük meg az alábbi Python kódot
def get_fraction(value, idx): arr = [4,5,2,0] # számok listája idx_value = arr[idx] # ha idx> arr hossza, IndexError fog jelentkezni value/idx_value # ha idx_value == 0, ZeroDivisionError fog jelentkezni if __name__ =='__main__': # set 'value' and 'idx' value = 54 idx = 3 # hívjuk a függvényt egy try-except utasításban. try: result = get_fraction(value, idx) print("Fraction is ", result) except except(IndexError, ZeroDivisionError) as ex: print(ex)
Két lehetséges kivételt lehet itt felhozni, ZeroDivisionError és IndexError Ha bármelyik kivétel felmerül, akkor a except blokk végrehajtásra kerül.
A fenti kódban idx=3, tehát idx_ érték 0 lesz, és érték /idx_ érték ZeroDivisionError hibát fog okozni
#2) Többszörös kivételek kezelése többszörös kivételekkel
try: #a kódod ide kerül except Exception1: #handle exception1 itt except Exception2: #handle exception2 itt except ExceptionN: #handle exceptionN itt
Ha inkább minden egyes kivételt külön-külön szeretnénk kezelni, akkor ezt így tehetjük meg.
Tekintsük az alábbi Python kódot
def get_fraction(value, idx): arr = [4,5,2,0] # számok listája idx_value = arr[idx] # ha idx> arr hossza, IndexError fog jelentkezni value/idx_value # ha idx_value == 0, ZeroDivisionError fog jelentkezni if __name__ =='__main__': # set 'value' and 'idx' value = 54 idx = 5 # hívjuk a függvényt egy try-excepts utasításban. try: result = get_fraction(value, idx) print("Fraction is ", result) exceptIndexError: print("idx of {} is out of range".format(idx)) except ZeroDivisionError: print("arr[{}] is 0. Hence, can't divide by zero".format(idx)) except Exception as ex: print(ex) print("Not sure what happened so not safe to continue, \ app will be interrupted") raise ex
Itt észrevehetjük, hogy az Exception-t az utolsó except utasításban használtuk. Ez azért van, mert az Exception kivételobjektum Exception bármelyik kivételre illik. Ezért mindig az utolsónak kell lennie, mivel a Python abbahagyja a többi kivételkezelő ellenőrzését, ha az egyik egyezik.
A fenti kódban, idx=5 , tehát arr[idx] emelni fogja IndexError mert idx nagyobb, mint a lista hossza arr
Továbbá, nem biztos, hogy az alkalmazás által generált kivétel soha nem biztonságos a végrehajtás folytatása. Ezért van az Exception típusunk, hogy minden előre nem látható kivételt elkapjunk. Ezután tájékoztatjuk a felhasználót, és megszakítjuk az alkalmazást ugyanazon kivétel generálásával.
Try Else utasítás
Ez egy opcionális funkció a kivételek kezelésének, és lehetővé teszi, hogy olyan kódot adjon hozzá, amelyet akkor szeretne lefuttatni, ha nem történt hiba. Ha hiba lép fel, ez az else-blokk nem fog lefutni.
Tekintsük az alábbi Python példakódot, nyissuk meg a szerkesztőnket, és mentsük el a kódot elseTry.py néven.
def fraction_of_one(divisor): value = 1/divisor # ha a divizor nulla, ZeroDivisionError fog jelentkezni return value if __name__ == '__main__': while True: try: # A felhasználótól kapunk bevitelt. # ha a bevitel nem érvényes argumentum az int() számára, ValueError fog jelentkezni divisor = int(input("Enter a divisor: ")) # hívjuk meg a függvényünket a tört kiszámításához value = fraction_of_one(divisor) except (ValueError,ZeroDivisionError): print("A bemenet nem lehet nulla, és az int() számára érvényes literálnak kell lennie. Kérjük, próbálja újra!") else: print("Érték: ", érték) break
Kapunk egy bemenetet a felhasználótól, és azt használjuk fel az 1 osztására. Két lehetséges kivétel van itt, egy érvénytelen felhasználói bemenet, ami a ValueError és egy zero(0) ami azt fogja okozni. ZeroDivisionError A except utasításunk kezeli ezeket a hibákat.
Most pedig ki akarjuk nyomtatni a érték Az else-blokkunk biztosítja, hogy csak akkor kerüljön kiírásra, ha a try blokkunk hiba nélkül végrehajtódik. Ez azért fontos, mert ha a try-blokkunkban hiba lép fel, akkor a érték nem lesz definiálva, így a hozzáférés újabb hibát fog okozni.
Futtassa a fenti kódot Python elseTry.py-val
A fenti kimenet azt mutatja, hogy az első bemenethez beírtuk, hogy 0 és megnyomta az ENTER-t. Mivel az osztónk 0-t kapott, az 1/divizor emelt zeroDivisionError A második bemenetünk k volt, ami érvénytelen a int (), ezért a kivétel ValueError felemelkedik.
De az utolsó bemenetünk 9 volt, ami érvényes, és ennek eredményeként megkaptuk az " érték " nyomtatott 0.111111111111111111111111111111
Próbáld ki Végül nyilatkozat
Ez is egy opcionális funkció a kivételkezelőkkel, és mindig futni fog, függetlenül attól, hogy mi történik a kivételkezelőkben.
Vagyis:
- Akár történik kivétel, akár nem
- Még akkor is, ha a többi blokkban egy 'return' meghívásra kerül.
- Még akkor is, ha a szkript kilép a többi blokkban
Tehát, ha van egy kódunk, amit minden helyzetben le akarunk futtatni, a finally-blokk a mi emberünk. Ezt a blokkot leginkább tisztításokra használjuk, például fájlok bezárására.
Tekintsük az alábbi Python kódot
def readFile(file_path): try: openFile = open(file_path,'r') # Fájl megnyitása csak olvasásra print(openFile.readline()) # A fájl tartalmának első sorának olvasása except FileNotFoundError as ex: print(ex) finally: print("Tisztítás...") openFile.close() if __name__ == '__main__': filePath = './text.txt' readFile(filePath)
Ez a kód megpróbálja megnyitni és beolvasni a text.txt fájlt az aktuális könyvtárban. Ha a fájl létezik, akkor a programunk kiírja a fájl első sorát, majd a finally blokk lefut és bezárja a fájlt.
Tegyük fel, hogy van egy text.txt nevű fájlunk abban a könyvtárban, ahol ez a programfájl van, és tartalmazza a Hello-t. Ha futtatjuk a programot, a következő kimenetet kapjuk
Ezt a példát szándékosan választottam, mert azt akartam, hogy foglalkozzunk egy kis problémával, ami akkor fordulhat elő, ha a finally-blokkban zárunk be fájlokat.
Ha a fájl nem létezik, akkor a kivétel FileNotFoundError megemelkedik, és a változó openFile nem lesz definiálva, és nem lesz fájlobjektum. Ezért ha megpróbáljuk lezárni a finally-blokkban, akkor egy kivételt fog kiváltani. UnboundLocalError amely a NameError .
Ez alapvetően azt mondja, hogy megpróbálunk hivatkozni a változóra openFile mielőtt azt kiosztották volna.
Egy kis trükk itt az, hogy a finally-blokkban kivételkezelőket használjunk.
def readFile(file_path): try: openFile = open(file_path,'r') # Fájl megnyitása csak olvasásra print(openFile.readline()) # A fájl tartalmának első sorának olvasása except FileNotFoundError as ex: print(ex) finally: try: print("Tisztítás...") openFile.close() except: # minden kivétel elkapása pass # Ezt a hibát ne vegyük figyelembe, mert nem érdekel minket. if __name__ == '__main__': filePath = './text.txt' readFile(filePath)
Ha a try-blokkunk FileNotFoundError-t ad ki, akkor a következő kimenetet kapjuk
Kivétel felvetése
A Python kivételek egyik jó híre, hogy szándékosan is fel tudjuk őket hívni. A kivételeket a emelési utasítás .
A raise utasítás szintaxisa a következő:
raise [ExceptionName[(*args: Object)]]
Nyisson meg egy terminált, és indítson el bármilyen kivételobjektumot a Python beépített kivételek közül. Például, ha ZeroDivisionError hibát okozunk:
>>> raise ZeroDivisionError("Nem lehet nullával osztani")
Megkapjuk a visszavezetést:
Miért fontos tehát a kivételek felemelése?
- Egyéni kivételekkel való munka során.
- A józansági ellenőrzések során.
Egyéni kivétel osztályok
Az egyéni kivétel olyan kivétel, amelyet a saját igényeinknek megfelelő hibák kezelésére hozunk létre. A trükk az, hogy definiálunk egy osztályt, amely az objektumtól származtatva a Kivétel , majd a raise utasítást használjuk a kivételosztályunk felemelésére.
Tegyük fel, hogy ellenőrizni akarjuk a felhasználói bemenetet, és meg akarunk győződni arról, hogy a bemeneti érték nem negatív (szanitási ellenőrzés). Persze, kiválthatnánk a Python kivétel ValueError-t, de szeretnénk testre szabni a hibát egy konkrét és önmagyarázó név megadásával, mint pl. InputIsNegativeError De ez a kivétel nem egy Python beépített kivétel.
Tehát először is létrehozzuk az alaposztályunkat, amely az Exception-ből fog származni.
class CustomError(Exception): "Az alaposztály kivétele a modul összes kivételéhez" pass
Ezután létrehozzuk a kivétel osztályunkat, amely örökli az alaposztályt, és kezeli a saját hibánkat.
class InputIsNegativeError(CustomError): """Raised when User enters a negative value""" pass
Teszteljük ezt
try: value = int(input()) if value <0: raise InputIsNegativeError # Kivétel, ha az érték negatív except InputIsNegativeError: # catch and handle exception print("Input value shouldn't be negative")
A fenti kód a felhasználói bemenetet kéri, és ellenőrzi, hogy az negatív-e. Ha igaz, akkor felveti az InputIsNegativeError nevű egyéni kivételünket, amelyet később az except utasításban elkapunk.
Az alábbiakban a teljes kódot találja:
class CustomError(Exception): "Alaposztály kivétel a modul összes kivételéhez" pass class InputIsNegativeError(CustomError): """Raised when User enters a negative value""" pass if __name__ == '__main__': try: value = int(input("Input a number: ")) if value <0: raise InputIsNegativeError # Kivétel, ha az érték negatív except InputIsNegativeError: # catch and handle exceptionprint("A bemeneti érték nem lehet negatív")
Ha a bemeneti érték egy negatív szám, például -1, akkor a kimenetet kapjuk:
A Python egyéni kivételekkel kapcsolatos további részletekért nézze meg a Python dokumentációt.
Gyakran ismételt kérdések
K #1) Hogyan kezeli a Python a kivételeket?
Válasz: A Python a kivételeket a try-except utasítás Az a kód, amely kivételt válthat ki, a következő helyen kerül elhelyezésre és végrehajtásra. próbálkozási blokk míg a kivéve a blokkot tartalmazza azt a kódot, amely a kivételeket kezeli, ha azok felmerülnek.
K #2) Mi az a kivétel felvetése Pythonban?
Válasz: Amikor a Python-értelmező érvénytelen kóddal találkozik, kivételt (exception) vet fel, ami a Python saját módja arra, hogy közölje velünk, hogy valami váratlan dolog történt. Kivételeket szándékosan is felvethetünk a emelési utasítás .
3. kérdés) Hogyan kezeli a Python a többszörös kivételeket?
Válasz: A Python a többszörös kivételeket egyetlen except blokk vagy több except blokk segítségével kezeli.
Egyetlen blokk esetében a kivételek egy tuple-ként kerülnek átadásra: kivéve (Exception1, Exception2,...,ExceptionN) és a Python jobbról balra haladva ellenőrzi az egyezést. Ebben az esetben minden egyes kivételre ugyanaz a művelet történik.
Egy másik módja az összes kivétel elkapásának, ha a kivétel nevét elhagyjuk az except kulcsszó után.
except: # itt kezeljük az összes kivételt
A második lehetőség, hogy minden egyes kivételhez egy except blokkot használunk:
except Exception1: # az Exception1 kezelésére szolgáló kód ide megy except Exception2: # az Exception2 kezelésére szolgáló kód ide megy except ExceptionN: # az ExceptionN kezelésére szolgáló kód ide megy
Így minden egyes Kivétel esetében külön-külön lépéseket tehet.
Q #4) Miért fontos a kivételek kezelése a Pythonban?
Válasz: A kivételek kezelésének előnye Pythonban az, hogy robusztus, tiszta és hibamentes alkalmazásokat hozhatunk létre. Nem akarjuk, hogy a produktív kódunk összeomoljon valamilyen hiba miatt, ezért kezeljük a hibákat, és így az alkalmazásunk továbbra is működőképes marad.
Q #5) Hogyan lehet figyelmen kívül hagyni egy kivételt Pythonban?
Válasz: Egy kivétel figyelmen kívül hagyásához Pythonban használjuk a passz kulcsszóval az except blokkban. Tegyük fel, hogy figyelmen kívül akarjuk hagyni a ValueError kivételt. Ezt így fogjuk megtenni:
except ValueError: pass
Hacsak nem tudod, mit csinálsz, rossz gyakorlat figyelmen kívül hagyni a kivételeket. Legalább tájékoztasd a felhasználót minden lehetséges hibáról.
Következtetés
Ebben a bemutatóban a következőket ismertettük: Python kivételek, Traceback; hogyan kezeljük a kivételeket a Próbálja ki a címet. / Kivéve / Else / Végül blokkok, hogyan kell Emelje fel a címet. Kivételek, és végül hogyan hozzunk létre saját egyéni kivételeket.
Köszönöm, hogy elolvastad!