Ovaj vodič objašnjava rukovanje iznimkama u Pythonu pomoću bloka Try Except uz pomoć primjera programiranja:
Dvije vrste grešaka mogu uzrokovati naglo zaustavljanje Python programa, tj. Sintaksa Greške i Izuzeci . U ovom vodiču ćemo raspravljati o drugom tipu greške (izuzeci) pod nekoliko važnih tema.
Dosta ćemo imati koristi od rukovanja izuzecima u našoj aplikaciji kao što su:
- Kreiranje robusne aplikacije.
- Kreiranje čistog koda bez grešaka.
Python Pokušajte osim
Jedna dobra vijest je da Python ima dobar broj ugrađenih izuzetaka za hvatanje grešaka u našem kodu. Takođe, daje nam mogućnost da kreiramo prilagođene izuzetke kada nijedan od ugrađenih izuzetaka ne odgovara našim potrebama.
Šta je izuzetak
Pa šta je izuzetak u Pythonu? Pa, jednostavno rečeno, kad god Python interpretator pokuša da izvrši nevažeći kod, on pokreće izuzetak, a u slučajevima kada se takav izuzetak ne obrađuje, ometa normalan tok programskih instrukcija i ispisuje povratni trag.
Kreirajmo nevažeći kod i vidimo kako će Python interpreter odgovoriti.
Otvorite Python ljusku i pokrenite sljedeći kod.
>>> 50/0
Ovo je jedan od najčešće greške u programiranju. Gornji kod pokušava podijeliti broj 50 sa 0 (nula). Pythonvarijabla openFile prije nego što je dodijeljena.
Mali trik ovdje je korištenje rukovatelja izuzetkom unutar finally-bloka.
def readFile(file_path): try: openFile = open(file_path,'r') # Open a file as read-only print(openFile.readline()) # Read first line of file content except FileNotFoundError as ex: print(ex) finally: try: print("Cleaning...") openFile.close() except: # catches all exceptions pass # Ignore this error because we don't care. if __name__ == '__main__': filePath = './text.txt' readFile(filePath)
Ako naš try-blok podiže FileNotFoundError, tada ćemo imati sljedeći izlaz
Podigni izuzetak
Jedna dobra vijest o Python izuzecima je da možemo namjerno podići ih. Izuzeci se podižu sa raise izrazom .
Izraz podizanja ima sljedeću sintaksu:
raise [ExceptionName[(*args: Object)]]
Otvorite terminal i podignite bilo koji objekt izuzetka iz Izuzeci ugrađeni u Python. Na primjer, ako podignemo ZeroDivisionError:
>>> raise ZeroDivisionError("Can't divide by zero")
Dobit ćemo povratni trag:
Dakle, zašto je važno podići izuzetke?
- Kada radite s prilagođenim izuzecima.
- Tokom provjera ispravnosti.
Prilagođene klase izuzetaka
Prilagođeni izuzetak je onaj koji kreirate za rukovanje greškama koje su specifične za vaše potrebe. Trik je u tome da definiramo klasu koja je izvedena iz objekta Exception , a zatim koristimo naredbu podizanja da podignemo našu klasu izuzetka.
Pretpostavimo da želimo provjeriti korisnički unos i osigurati ulazna vrijednost nije negativna (provjera ispravnosti). Naravno, mogli bismo podići Python izuzetak ValueError, ali bismo željeli prilagoditi grešku dajući joj specifično i samoobjašnjavajuće ime kao što je InputIsNegativeError . Ali ovaj izuzetak nije ugrađen u PythonIzuzetak.
Dakle, prvo kreiramo našu osnovnu klasu koja će proizaći iz Exceptiona.
class CustomError(Exception): "Base class exception for all exceptions of this module" pass
Zatim kreiramo našu klasu izuzetaka koja će naslijediti osnovnu klasu i koja će rješavati našu specifičnu grešku.
class InputIsNegativeError(CustomError): """Raised when User enters a negative value""" pass
Hajde da testiramo ovo
try: value = int(input()) if value 0: raise InputIsNegativeError # Raise exception if value is negative except InputIsNegativeError: # catch and handle exception print("Input value shouldn't be negative")
Gorenji kodni zahtjev za korisnički unos, i provjerimo da li je negativan. Ako je istinito, podiže naš prilagođeni izuzetak InputIsNegativeError koji se kasnije hvata u naredbi osim.
Ispod je kompletan kod:
class CustomError(Exception): "Base class exception for all exceptions of this module" 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 # Raise exception if value is negative except InputIsNegativeError: # catch and handle exception print("Input value shouldn't be negative")
Ako je ulazna vrijednost je negativan broj poput -1, tada ćemo imati izlaz:
Pogledajte Python dokument za više detalja o Python prilagođenim izuzecima.
Često postavljana pitanja
P #1) Kako Python rješava izuzetak?
Odgovor: Python rješava izuzetke koristeći try-except izraz . Kôd koji može pokrenuti izuzetak se postavlja i izvršava u blok pokušaja dok izuzev blok sadrži kod koji će rukovati iznimkama ako do njih dođe.
P #2) Šta je podizanje izuzetka u Pythonu?
Odgovor: Kad god Python interpretator naiđe na nevažeći kod, pokreće izuzetak, što je Pythonov vlastiti način da nam kaže da se dogodilo nešto neočekivano. Također možemo namjerno podići izuzetke koristeći raise izraz .
P #3) Kako Python obrađuje više izuzetaka?
Odgovor: Python obrađuje više izuzetakakoristeći ili jedan blok osim ili više blokova osim blokova.
Za jedan blok, izuzeci se prosljeđuju kao tuple: osim (Exception1, Exception2,..,ExceptionN) i Python provjere za utakmicu s desna na lijevo. U ovom slučaju, ista akcija se poduzima za svaki izuzetak.
Drugi način da uhvatite sve izuzetke je da izostavite ime izuzetka iza ključne riječi osim.
except: # handle all exceptions here
Drugi način je da koristite blok osim za svaki izuzetak:
except Exception1: # code to handle Exception1 goes here except Exception2: # code to handle Exception2 goes here except ExceptionN: # code to handle ExceptionN goes here
Na ovaj način možete preduzeti zasebne akcije za svaki izuzetak.
P #4) Zašto je rukovanje iznimkama važno u Pythonu?
Odgovor: Prednost rukovanja izuzecima u Pythonu je da možemo kreirati robusne, čiste aplikacije bez grešaka. Ne želimo da se naš proizvodni kod sruši zbog nekih grešaka, tako da rješavamo greške i održavamo našu aplikaciju u funkciji.
P #5) Kako zanemariti izuzetak u Pythonu?
Odgovor: Da biste zanemarili izuzetak u Pythonu, koristite ključnu riječ pass u bloku osim. Recimo da želimo zanemariti izuzetak ValueError. Uradićemo to na sledeći način:
except ValueError: pass
Osim ako ne znate šta radite, loša je praksa ignorisati izuzetke. Barem obavijestite korisnika o svim potencijalnim greškama.
Zaključak
U ovom vodiču smo pokrili: Python izuzeci, Traceback; kako rukovati izuzecima sa Pokušaj / Osim / Else / Konačno blokove, kako Povisiti izuzetke i konačno kako kreirati vlastite prilagođene izuzetke.
Hvala na čitanju!
interpretator vidi ovo kao nevažeću operaciju i podiže ZeroDivisionError, ometa program i ispisuje povratni trag.
Možemo jasno vidjeti da ZeroDivisionError je izuzetak koji je podignut. To je zaista Python-ov vlastiti način da nam kaže da nije cool dijeliti broj sa nulom. Iako u drugim jezicima kao što je JavaScript, ovo nije greška; a python striktno zabranjuje ovu praksu.
Također, važno je znati da je ovo samo objekt izuzetka i da Python ima mnogo takvih objekata ugrađenih. Pogledajte ovu zvaničnu dokumentaciju za Python da vidite sve Python ugrađene iznimke.
Razumijevanje Traceback-a
Prije nego što krenemo u rukovanje izuzecima, mislim da će nam pomoći da shvatimo šta će se tačno dogoditi ako izuzeci se ne obrađuju i kako Python daje sve od sebe da nas obavijesti o našoj grešci.
Kad god Python naiđe na grešku, pokreće izuzetak. Ako se ovaj izuzetak ne obradi, onda proizvodi neke informacije koje se nazivaju Traceback. Dakle, koje informacije sadrži ovo praćenje?
Sadrži:
- Poruku o grešci koja nam govori koji je izuzetak pokrenut i šta se dogodilo prije nego što je ovaj izuzetak bio podignuto.
- Različiti brojevi redova koda koji su uzrokovali ovu grešku. Greška može biti uzrokovana nizom poziva funkcija koji se nazivaju stog poziva o čemu ćemo kasnije ovdje raspravljati.
Iako je tomalo zbunjujuće, obećavamo da će sljedeći primjer donijeti više svjetla našem razumijevanju.
Prisjetite se povratnog traga koji je odštampan dijeljenjem 50 sa 0 iznad, možemo vidjeti da povratni primjer sadrži sljedeće informacije:
- Datoteka “”: Ovo nam govori da je ovaj kod pokrenut sa terminala konzole.
- red 1: Ovo nam govori da se greška dogodila u ovom broju reda.
- ZeroDivisionError: podjela na nula: To nam govori koji je izuzetak pokrenut i što ga je uzrokovalo.
Pokušajmo s drugim primjerom i možda vidjeti kako izgleda stek poziva . Otvorite uređivač, unesite kod ispod i sačuvajte kao 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
Otvorite terminal u direktoriju u kojem je ova datoteka pronađena i pokrenite.
python tracebackExp.py
Vidjet ćete sljedeću pozadinu:
Navedeno praćenje može izgledati zbunjujuće, ali zapravo nije. Pythonistas su smislili najbolji način za čitanje povratnog traga, koji je od odozdo prema gore . Dakle, upotrijebimo ovu mudrost da pokušamo razumjeti šta ovaj traceback može ponuditi.
- Na dnu, dobijamo izuzetak koji je podignut i zašto je podignut.
- Prelazeći nagore, dobijamo naziv datoteke tracebackExp .py gdje se ova greška dogodila, izračunavanje koje je uzrokovalo ovu grešku compute = numb/div, stack funkcija2 i red broj linka 6 gdje je ovo izračunavanje obavljeno .
- Pomaknuvši se prema gore, vidimo da naša funkcija stack2je pozvan u funkcijski stog1 u redu broj 3.
- Prelazeći na vrh, vidimo da je funkcija stack1 pozvana u redu broj 11. module > govori nam da je to datoteka koja se izvršava.
Uobičajeni Python izuzeci
Python biblioteka definira strašno puno ugrađenih izuzetaka. Možete provjeriti Python dokumentaciju ili pozvati ugrađenu local () funkciju na sljedeći način:
>>> dir(locals()['__builtins__'])
Nećemo pokušavati riješiti sve ove izuzetke, ali ćemo vidjeti nekoliko uobičajenih izuzetaka na koji ćete vjerovatno naići.
#1) TypeError
Pojavljuje se kada se operacija ili funkcija primjenjuje na objekt neprikladnog tipa.
Primjer 1
Razmotrite donji program. Uzima dividendu i djelitelj, zatim izračunava i ispisuje rezultat dijeljenja dividende djelikom.
def compute_division(): dividend = int(input("Enter the dividend: ")) # cast string to int divisor = input("Enter the divisor: ") # no casting # Compute division result = dividend/divisor # print result print("The result of {}/{} is: {}".format(dividend, divisor, result)) if __name__ == '__main__': result = compute_division()
Zahtijevamo vrijednost dividende i djelitelja od korisnika, ali zaboravljamo baciti string djelitelja vrijednost u cijeli broj. Dakle, na kraju ćemo imati tip dividende cijeli broj ( int ), a tip djelitelja string ( str ). Tada dobijamo TypeError jer operator podjele (/) ne radi na nizovima.
Možda će vas zanimati da za razliku od Pythona, Javascript ima prinudu tipa koja u osnovi pretvara jedan od tipova operanda u ekvivalentnu vrijednost tipa drugog operanda kada su operandi jednakirazličiti tipovi.
#2) ValueError
Ovo se javlja kada operacija ili funkcija primi argument koji ima ispravan tip, ali neprikladnu vrijednost.
Primjer 2
Razmotrite naš program u Primjeru 1 iznad.
Ako korisnik unese alfanumeričku vrijednost za dividendu kao što je '3a', onda će naš program podići izuzetak ValueError. To je zato što, iako metoda Python int() uzima bilo koji broj ili niz i vraća cjelobrojni objekt, vrijednost stringa ne bi trebala sadržavati slova ili bilo koju nenumeričku vrijednost.
#3) AttributeError
Ovaj izuzetak se javlja prilikom dodjeljivanja ili upućivanja na atribut koji ne postoji.
Primjer 3
Razmotrite program ispod. Uzima broj i izračunava njegov kvadratni korijen koristeći Python matematički modul
import math # import math library to gain access to its code def compute_square_root(number): # compute the square root using the math library result = math.sqr(number) return result if __name__ == '__main__': # get input to compute from user number = int(input("Compute Square root of: ")) # call function to compute square root
Kada korisnik unese broj, naš program pokušava koristiti funkciju iz matematičkog modula da izračuna svoj kvadratni korijen, ali samo to ovdje, napravili smo grešku. Umjesto sqrt, greškom smo upisali sqr koji ne postoji u matematičkom modulu.
Dakle, pokušavali smo referencirati atribut sqr koji ne postoji i vodili smo na izuzetak AttributeError koji se podiže. Većina nas često pravi ovu vrstu greške. Dakle, niste sami.
Rukovanje izuzecima sa Try Except
Kao programer, jedna stvar na koju će većina nas trošiti svoje vrijeme je pisanje robusnog koda koji jeotporan. Kod koji se ne kvari zbog nekih grešaka. U Pythonu to možemo postići zatvaranjem naših naredbi unutar naredbe try – except .
Python Try-Except izraz
Naredba try-except ima sljedeću strukturu:
try: #your code goes here except """Specify exception type(s) here""": #handle exception here
Ugradimo kod u tracebackExp .py unutar naredbe try-except.
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 continuous") # 15
Pokretanje ovog koda će proizvesti izlaz
Ovako radi naredba try-except. Python izvršava kod u bloku pokušaja red 7-8 . Ako se ne pronađe nevažeći kod, tada se kod u bloku osim red 10 preskače i izvršavanje se nastavlja.
Ali, ako se pronađe nevažeći kod, tada se izvršenje odmah zaustavlja u try blok i provjerava da li se podignuti izuzetak podudara s onim koji smo dali u naredbi osim red 9 . Ako se podudara, tada se blok osim izvršava i nastavlja. Ako se to ne dogodi, program će prekinuti.
Blok pokušaja obično sadrži kod koji može pokrenuti izuzetak dok blok izuzetak hvata i obrađuje izuzetak.
Rukovanje višestrukim Izuzeci sa izuzetkom
Možemo rukovati više izuzetaka sa jednim “osim” ili višestrukim “osim”. Sve zavisi od toga kako želite da rukujete svaki izuzetak.
#1) Rukovanje više izuzetaka sa jednim osim
try: #your code goes here except(Exception1[, Exception2[,...ExceptionN]]]): #handle exception here
Ova metoda se koristi kada sumnjamo da bi naš kod mogaopodići različite izuzetke i želimo poduzeti istu akciju u svakom slučaju. Dakle, ako Python interpretator pronađe podudaranje, tada će se izvršiti kod napisan u bloku osim.
Razmotrimo primjer Python koda ispod
def get_fraction(value, idx): arr = [4,5,2,0] # a list of numbers idx_value = arr[idx] # if idx is > arr length, IndexError will be raised value/idx_value # if idx_value == 0, ZeroDivisionError will be raised if __name__ =='__main__': # set 'value' and 'idx' value = 54 idx = 3 # call function in a try-except statement. try: result = get_fraction(value, idx) print("Fraction is ", result) except (IndexError, ZeroDivisionError) as ex: print(ex)
Imamo dva mogući izuzeci koji se ovdje mogu pokrenuti, ZeroDivisionError i IndexError . Ako se pojavi bilo koji od ovih izuzetaka, tada će se izvršiti blok osim.
U kodu iznad, idx=3, tako da idx_ vrijednost postaje 0 i vrijednost /idx_ value će podići ZeroDivisionError
#2) Rukovanje višestrukim izuzecima s višestrukim izuzecima
try: #your code goes here except Exception1: #handle exception1 here except Exception2: #handle exception2 here except ExceptionN: #handle exceptionN here
Ako bismo radije htjeli rukovati svaki izuzetak zasebno, onda to možete učiniti ovako.
Razmotrite primjer Python koda ispod
def get_fraction(value, idx): arr = [4,5,2,0] # a list of numbers idx_value = arr[idx] # if idx is > arr length, IndexError will be raised value/idx_value # if idx_value == 0, ZeroDivisionError will be raised if __name__ =='__main__': # set 'value' and 'idx' value = 54 idx = 5 # call function in a try-excepts statement. try: result = get_fraction(value, idx) print("Fraction is ", result) except IndexError: 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
Ovdje primjećujemo da je izuzetak korišten u posljednjoj izjavi osim . To je zato što objekt izuzetka Exception odgovara bilo kojem izuzetku. Iz tog razloga, uvijek bi trebao biti posljednji, pošto će Python prestati provjeravati druge rukovaoce izuzecima kada se jedan podudara.
U kodu iznad, idx=5 , dakle arr[idx ] će podići IndexError jer je idx veći od dužine liste arr
Također, niste sigurni koji izuzetak je pokrenula vaša aplikacija nikada nije siguran za nastavak izvršavanja. Zato imamo tip Exception da uhvatimo sve nepredviđene izuzetke. Zatim obavještavamokorisnika i prekinuti aplikaciju podizanjem istog izuzetka.
Pokušajte Else naredbu
Ovo je opciona funkcija rukovanja izuzetkom i omogućava vam da dodate kod koji želite pokrenuti kada nije došlo do greške. Ako dođe do greške, ovaj else-blok se neće pokrenuti.
Razmotrite primjer Python koda ispod, otvorite svoj uređivač i sačuvajte kod kao elseTry.py
def fraction_of_one(divisor): value = 1/divisor # if divisor is zero, ZeroDivisionError will be raised return value if __name__ == '__main__': while True: try: # Get input from the user. # if input is not a valid argument for int(), ValueError will be raised divisor = int(input("Enter a divisor: ")) # call our function to compute the fraction value = fraction_of_one(divisor) except (ValueError, ZeroDivisionError): print("Input can't be zero and should be a valid literal for int(). Please, try again!") else: print("Value: ", value) break
Dobijamo unos od korisnika i koristimo ga da podijelimo 1. Ovdje imamo dva moguća izuzetka, nevažeći korisnički unos koji će uzrokovati ValueError i nula(0) koji će uzrokovati ZeroDivisionError . Naš izraz osim rješava ove greške.
Sada želimo ispisati vrijednost vrijednosti . Naš else-blok osigurava da je ispisan samo ako se naš blok try izvrši bez greške. Ovo je važno jer ako dođe do greške u našem bloku pokušaja, vrijednost će biti nedefinirana. Dakle, pristup će izazvati još jednu grešku.
Pokrenite gornji kod sa Python elseTry.py
Gornji izlaz pokazuje da za prvi unos, otkucali smo 0 i pritisnuli ENTER. Pošto je naš djelitelj primio 0, 1/djelitelj je podigao zeroDivisionError . Naš drugi unos je bio k što je nevažeće za int (), stoga je podignut izuzetak ValueError .
Ali naš posljednji unos je bio 9 što je važeće i kao rezultat, dobili smo vrijednost “ value ” ispisanu kao 0.11111111111111111
Pokušajte konačnoNaredba
Ovo je također opciona karakteristika rukovanja iznimkama i uvijek će se pokrenuti bez obzira na to što se dogodi u obrađivačima izuzetaka.
To je:
- Da li se dogodi izuzetak ili ne
- Čak i ako je 'povratak' pozvan u drugim blokovima.
- Čak i ako je skripta prekinuta u drugim blokovima
Dakle, ako imamo kod koji želimo pokrenuti u svim situacijama, konačno-block je naš tip. Ovaj blok se uglavnom koristi za čišćenje poput zatvaranja datoteka.
Razmotrite primjer Python koda ispod
def readFile(file_path): try: openFile = open(file_path,'r') # Open a file as read-only print(openFile.readline()) # Read first line of file content except FileNotFoundError as ex: print(ex) finally: print("Cleaning...") openFile.close() if __name__ == '__main__': filePath = './text.txt' readFile(filePath)
Ovaj kod pokušava otvoriti i pročitati datoteku text.txt u svom trenutnom direktoriju. Ako datoteka postoji, onda će naš program ispisati prvi red datoteke, a zatim će se pokrenuti naš finally-blok i zatvoriti datoteku.
Recimo da imamo datoteku koja se zove text.txt u direktoriju gdje je ovaj programski fajl je i sadrži Hello. Ako pokrenemo program, imat ćemo izlaz
Ovaj primjer je odabran namjerno jer sam želio da riješimo mali problem koji se može pojaviti prilikom zatvaranja datoteka u konačno- blok.
Ako datoteka ne postoji, izuzetak FileNotFoundError će biti podignut i varijabla openFile neće biti definirana i neće biti datoteka objekt. Stoga, pokušaj da ga zatvorite u finally-bloku će pokrenuti izuzetak UnboundLocalError koji je podklasa NameError .
Ovo u osnovi govori da pokušavamo referencirati the