See õpetus selgitab erandite käsitlemist Pythonis Try Except ploki abil programmeerimisnäidete abil:
Kaks veatüüpi võivad põhjustada Pythoni programmi järsu peatumise, st. Süntaksi vead ja Erandid Selles õpetuses käsitleme teist veatüüpi (Erandid) mitme olulise teema all.
Me saame palju kasu erandite käsitlemisest meie rakenduses, näiteks:
- Tugeva rakenduse loomine.
- Puhta ja veavaba koodi loomine.
Python Proovi Välja arvatud
Üks hea uudis on see, et Pythonil on hea hulk sisseehitatud erandeid, et püüda vigu meie koodis. Samuti annab see meile võimaluse luua kohandatud erandeid, kui ükski sisseehitatud eranditest ei sobi meie vajadustele.
Mis on erand
Mis on siis Pythonis erand? Lihtsustatult öeldes, kui Pythoni interpretaator üritab täita vigast koodi, tekitab ta erandi ja juhul, kui sellist erandit ei käsitleta, katkestab ta programmi käskude normaalse kulgemise ja trükkib tagasiside.
Loome kehtetu koodi ja vaatame, kuidas Pythoni interpretaator reageerib.
Avage Pythoni käsurea ja käivitage järgmine kood.
>>>> 50/0
See on üks levinumaid vigu programmeerimisel. Ülaltoodud kood püüab jagada arvu 50 poolt 0 (null). Pythoni interpretaator näeb seda kehtetu operatsioonina ja tekitab teate ZeroDivisionError , katkestab programmi ja väljastab tagasiside.
Me näeme selgelt, et ZeroDivisionError on erand, mis tekkis. See on tõepoolest Pythoni enda viis öelda meile, et arvu nulliga jagamine ei ole lahe. Kuigi teistes keeltes, näiteks JavaScriptis, ei ole see viga; ja python keelab selle praktika rangelt ära.
Samuti on oluline teada, et see on lihtsalt erandite objekt ja Pythonil on sisseehitatud palju selliseid objekte. Vaata siit Pythoni ametlikust dokumentatsioonist kõiki Pythoni sisseehitatud erandeid.
Tracebacki mõistmine
Enne kui me hakkame erandeid käsitlema, on minu arvates kasulik mõista, mis täpselt juhtub, kui erandeid ei käsitleta, ja kuidas Python teeb kõik endast oleneva, et meid meie veast teavitada.
Iga kord, kui Python kohtab viga, tekitab ta erandi. Kui seda erandit ei käsitleta, siis toodab ta mingi teabe, mida nimetatakse Traceback. Millist teavet see traceback siis sisaldab?
See sisaldab:
- Veateade, mis ütleb meile, milline erand tekkis ja mis juhtus enne selle erandi tekkimist.
- Vea põhjustanud koodi erinevad rea numbrid. Vea võib põhjustada funktsioonikutsete jada, mida nimetatakse kõnede virna mida arutame siinkohal hiljem.
Kuigi see on veidi segane, lubame, et järgmine näide toob meie arusaamisele rohkem valgust.
Tuletame meelde tagasiside, mis trükiti eespool 50-i jagamisel 0-ga, näeme, et tagasiside sisaldab järgmist teavet:
- Fail "": See ütleb meile, et see kood käivitati konsooliterminalist.
- rida 1: See ütleb meile, et viga tekkis sellel rea numbril.
- ZeroDivisionError: jagunemine null: See ütleb meile, milline erand tekkis ja mis selle põhjustas.
Proovime veel ühte näidet ja vaatame ehk, kuidas üks kõnede virna näeb välja. Avage toimetaja, sisestage alljärgnev kood ja salvestage kui 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
Avage terminal kataloogis, kus see fail asub, ja käivitage see.
python tracebackExp.py
Näete järgmist jälgimisjälge:
Ülaltoodud traceback võib tunduda segane, kuid tegelikult ei ole see seda. Pythonistas tuli välja parim viis tracebacki lugemiseks, mis on pärit alt üles Niisiis, kasutame seda tarkust, et püüda mõista, mida see jälgimine pakub.
- Kõige allosas kuvatakse, milline erand tekkis ja miks see tekkis.
- Edasi liikudes saame faili nime tracebackExp .py, kus see viga tekkis, arvutused, mis põhjustasid selle vea compute = numb/div, funktsioon stack2 ja link number rida 6, kus see arvutus tehti.
- Edasi liikudes näeme, et meie funktsiooni stack2 kutsuti funktsioonis stack1 reas number 3.
- Liikudes kõige ülemisele, näeme, et funktsiooni stack1 kutsuti reas number 11. < moodul > ütleb meile, et tegemist on failiga, mida täidetakse.
Levinud Pythoni erandid
Pythoni raamatukogu määratleb kohutavalt palju sisseehitatud erandeid. Saate vaadata Pythoni dokumentatsiooni või kutsuda sisseehitatud kohalik () funktsioon nagu allpool:
>>> dir(locals()['__builtins__'])
Me ei püüa käsitleda kõiki neid erandeid, kuid vaatleme mõningaid levinud erandeid, millega te tõenäoliselt kokku puutute.
#1) TypeError
See ilmneb, kui operatsiooni või funktsiooni rakendatakse sobimatut tüüpi objektile.
Näide 1
Vaadake alljärgnevat programmi. See võtab vastu dividendi ja divisendi, seejärel arvutab ja väljastab dividendi ja divisori jagamise tulemuse.
def compute_division(): dividend = int(input("Sisesta dividend: ")) # cast string to int divisor = input("Sisesta 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()
Me küsime kasutajalt dividendi ja divisori väärtust, kuid unustame castida divisori stringi väärtuse täisarvuks. Nii saame dividendi tüübiks täisarvu( int ) ja jagaja tüüp on string( str ). Seejärel saame TypeError kuna jagamisoperaator (/) ei toimi stringidega.
Võib-olla on teile huvitav teada, et erinevalt Pythonist on Javascriptis Type Coercion, mis põhimõtteliselt teisendab ühe operandi tüübi teise operandi tüübi samaväärseks väärtuseks, kui operandid on erinevat tüüpi.
#2) ValueError
See ilmneb, kui operatsioon või funktsioon saab argumendi, millel on õige tüüp, kuid sobimatu väärtus.
Näide 2
Vaadake meie programmi Näide 1 eespool.
Kui kasutaja sisestab dividendi jaoks tähtnumbrilise väärtuse nagu '3a', siis meie programm tekitab ValueError erandi. See on tingitud sellest, et kuigi Pythoni int() meetod võtab vastu mis tahes arvu või stringi ja tagastab täisarvu objekti, ei tohiks stringi väärtus sisaldada tähti või muid mitte-numbrilisi väärtusi.
#3) AttributeError
See erand tekib atribuudi määramisel või viitamisel atribuudile, mida ei ole olemas.
Näide 3
Vaadake alljärgnevat programmi. See võtab sisse arvu ja arvutab selle ruutjuure, kasutades Pythoni matemaatikamoodulit.
import math # import math library, et saada ligipääs selle koodile def compute_square_root(number): # arvutame ruutjuure kasutades math library result = math.sqr(number) return result if __name__ == '__main__': # saame kasutajalt sisendi arvutamiseks number = int(input("Compute Square root of: ")) # kutsume funktsiooni ruutjuure arvutamiseks välja
Kui kasutaja sisestab arvu, püüab meie programm kasutada matemaatikamooduli funktsiooni, et arvule ruutjuurt arvutada, kuid just siin tegime vea. sqrt asemel sisestasime ekslikult sqr, mida matemaatikamoodulis ei ole olemas.
Niisiis, me püüdsime viidata atribuudile sqr, mida ei ole olemas, ja see tõi kaasa erandi AttributeError tekkimise. Enamik meist teeb selliseid vigu palju. Nii et te ei ole üksi.
Erandite käsitlemine Try Exceptiga
Programmeerijana on üks asi, millele enamik meist kulutab oma aega, robustse koodi kirjutamine, mis on vastupidav. Kood, mis ei katkea mõne vea tõttu. Pythonis saame seda saavutada, kui ümbritseme oma avaldused sees oleva proovige - välja arvatud avaldus.
Pythoni Try-Except avaldis
Try-except avaldusel on järgmine struktuur:
try: #oma kood läheb siia except """Specify exception type(s) here""": #handle exception here
Lisame koodi sisse tracebackExp .py try-except avalduse sees.
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("programm pidev") # 15
Selle koodi käivitamine annab tulemuseks
Nii töötab try-except avaldis. Python täidab koodi try-blokis. rida 7-8 Kui kehtetut koodi ei leita, siis on plokis except olev kood rida 10 jäetakse vahele ja täitmine jätkub.
Kui aga leitakse vigane kood, siis peatub täitmine kohe try-blokis ja kontrollitakse, kas tekitatud erand vastab sellele, mille me esitasime except-avalduses rida 9 Kui see vastab, siis täidetakse erandiblokk ja jätkatakse. Kui see ei vasta, siis katkestab programm.
Try-blokk sisaldab tavaliselt koodi, mis võib tekitada erandi, samal ajal kui except-blokk püüab ja käsitleb erandit.
Mitme erandi käsitlemine Exceptiga
Me saame käsitleda mitut erandit kas ühe "except" või mitme "except" abil. Kõik sõltub sellest, kuidas soovite iga erandit käsitleda.
#1) Mitme erandi käsitlemine ühe erandiga
try: #oma kood läheb siia except(Exception1[, Exception2[,...ExceptionN]]]): #handle exception here
Seda meetodit kasutatakse siis, kui me kahtlustame, et meie kood võib tekitada erinevaid erandeid ja me tahame igal juhul teha sama toimingu. Seega, kui Pythoni interpretaator leiab kokkulangevuse, siis täidetakse except-blokki kirjutatud kood.
Vaatleme alljärgnevat Python-koodi näidet
def get_fraction(value, idx): arr = [4,5,2,0] # numbrite nimekiri idx_value = arr[idx] # kui idx on> arr pikkus, tekib IndexError value/idx_value # kui idx_value == 0, tekib ZeroDivisionError if __name__ =='__main__': # set 'value' ja 'idx' value = 54 idx = 3 # kutsume funktsiooni try-except avalduses. try: result = get_fraction(value, idx) print("Fraction is ", result) except(IndexError, ZeroDivisionError) as ex: print(ex)
Meil on kaks võimalikku erandit, mida võiks siinkohal tõstatada, ZeroDivisionError ja IndexError Kui mõni neist eranditest tõuseb, siis käivitatakse except-blokk.
Ülaltoodud koodis on idx=3, seega idx_ väärtus muutub 0-ks ja väärtus /idx_ väärtus tekitab ZeroDivisionError
#2) Mitmete erandite käsitlemine mitme erandiga
try: #oma kood läheb siia except Exception1: #käsitle exception1 siin except Exception2: #käsitle exception2 siin except ExceptionN: #käsitle exceptionN siin
Kui me tahame pigem iga erandit eraldi käsitleda, siis saab seda teha nii.
Vaadake alljärgnevat Python-koodi näidet
def get_fraction(value, idx): arr = [4,5,2,0] # numbrite nimekiri idx_value = arr[idx] # kui idx on> arr pikkus, tekib IndexError value/idx_value # kui idx_value == 0, tekib ZeroDivisionError if __name__ =='__main__': # set 'value' and 'idx' value = 54 idx = 5 # kutsume funktsiooni try-excepts avalduses. 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("Ei ole kindel, mis juhtus, seega ei ole turvaline jätkata, \ app will be interrupted") raise ex
Me märkame siin, et Exception kasutati viimases except-avalduses. See on tingitud sellest, et erandiobjekt Exception vastab igale erandile. Sel põhjusel peaks see olema alati viimane, kuna Python lõpetab teiste erandikäsitlejate kontrollimise, kui üks neist vastab.
Ülaltoodud koodis, idx=5 seega arr[idx] tõstab IndexError sest idx on suurem kui nimekirja pikkus arr
Samuti ei ole kindel, mis erandit teie rakendus tekitas, kunagi ohutu jätkata täitmist. Sellepärast on meil tüüp Exception, et püüda kõik ootamatud erandid. Seejärel teavitame kasutajat ja katkestame rakenduse, tõstes sama erandi.
Try Else avaldus
See on valikuline funktsioon erandite käsitlemise ja võimaldab lisada koodi, mida soovite käivitada, kui vigu ei esine. Kui viga esineb, siis see else-blokk ei käivitu.
Vaadake alljärgnevat Python-koodi, avage oma redaktor ja salvestage kood elseTry.py kujul.
def fraction_of_one(divisor): value = 1/divisor # kui divisor on null, tekib ZeroDivisionError return value if __name__ == '__main__': while True: try: # saada kasutajalt sisend. # kui sisend ei ole kehtiv argument int() jaoks, tekib ValueError divisor = int(input("Enter a divisor: ")) # kutsume meie funktsiooni, et arvutada fraktsioon value = fraction_of_one(divisor) except (ValueError,ZeroDivisionError): print("Sisend ei saa olla null ja peaks olema int() jaoks kehtiv literaal. Palun proovige uuesti!") else: print("Väärtus: ", väärtus) break
Saame kasutajalt sisendi ja kasutame seda 1 jagamiseks. Meil on siin kaks võimalikku erandit, kehtetu kasutaja sisend, mis põhjustab ValueError ja null(0) mis põhjustab ZeroDivisionError . Meie except-deklaratsioon käsitleb neid vigu.
Nüüd tahame välja printida väärtuse väärtus Meie else-blokk tagab, et see trükitakse ainult siis, kui meie try-blokk käivitub ilma veata. See on oluline, sest kui meie try-blokis tekib viga, siis on väärtus on määratlemata. Seega tekitab sellele ligipääs järjekordse vea.
Käivita ülaltoodud kood Pythoni elseTry.py abil
Ülaltoodud väljund näitab, et esimese sisendi jaoks sisestasime 0 ja vajutasime ENTER. Kuna meie divisor sai 0, siis 1/divisor tõstis zeroDivisionError Meie teine sisend oli k, mis on kehtetu. int (), seega erand ValueError tõstetakse.
Kuid meie viimane sisend oli 9, mis on kehtiv ja selle tulemusena saime väärtuseks " väärtus " trükitud kujul 0.1111111111111111111111111111
Proovige lõpuks avaldust
See on ka valikuline funktsioon erandite käitlemise ja töötab alati, olenemata sellest, mis juhtub erandite käitlejatega.
See tähendab:
- Kas erand esineb või mitte
- Isegi kui teistes plokkides kutsutakse 'return'.
- Isegi kui skript on lõpetatud teistes plokkides
Niisiis, kui meil on kood, mida me tahame kõigis olukordades käivitada, on finally-block meie mees. Seda plokki kasutatakse enamasti puhastustöödeks, näiteks failide sulgemiseks.
Vaadake alljärgnevat Python-koodi näidet
def readFile(file_path): try: openFile = open(file_path,'r') # Ava faili ainult lugemiseks print(openFile.readline()) # Loe faili sisu esimene rida except FileNotFoundError as ex: print(ex) finally: print("Puhastamine...") openFile.close() if __name__ == '__main__': filePath = './text.txt' readFile(filePath)
See kood üritab avada ja lugeda faili text.txt oma praeguses kataloogis. Kui fail on olemas, siis meie programm trükib faili esimese rea, seejärel käivitub meie finally-blokk ja sulgeb faili.
Oletame, et meil on fail nimega text.txt kataloogis, kus see programmifail asub ja sisaldab Hello. Kui me käivitame programmi, siis saame väljundiks
See näide valiti meelega, sest ma tahtsin, et me käsitleksime väikest probleemi, mis võib tekkida failide sulgemisel finally-blokis.
Kui faili ei eksisteeri, tekib erand FileNotFoundError tõstetakse ja muutuja openFile ei ole defineeritud ja see ei ole failiobjekt. Seega, kui seda püütakse sulgeda finally-blokis, tekib erand UnboundLocalError mis on alamklass NameError .
See ütleb põhimõtteliselt, et me üritame viidata muutujale openFile enne selle määramist.
Väike trikk on siinkohal kasutada erandite käitlejaid finally-bloki sees.
def readFile(file_path): try: openFile = open(file_path,'r') # Ava faili ainult lugemiseks print(openFile.readline()) # Loe faili sisu esimene rida except FileNotFoundError as ex: print(ex) finally: try: print("Puhastamine...") openFile.close() except: # püüab kõik erandid ära # Ignoreeri see viga, sest me ei hooli. if __name__ == '__main__': filePath = './text.txt' readFile(filePath)
Kui meie try-blokk tekitab FileNotFoundError'i, siis saame järgmise väljundi
Tõsta erand
Üks hea uudis Pythoni erandite kohta on see, et me saame neid tahtlikult esile kutsuda. Erandid kutsutakse esile käsuga raise avaldus .
Raise-avaldusel on järgmine süntaks:
raise [ExceptionName[(*args: Object)]]
Avage terminal ja tõstke Pythoni sisseehitatud erandite hulgast ükskõik milline erandiobjekt. Näiteks, kui tekitame ZeroDivisionError:
>>> raise ZeroDivisionError("Ei saa jagada nulliga")
Me saame jälgimisjälje:
Miks on siis oluline tõsta erandeid?
- Kui töötate kohandatud eranditega.
- Mõistlikkuse kontrollimisel.
Kohandatud erandiklassid
Kohandatud erand on selline, mille te loote, et käsitleda vigu, mis on konkreetselt teie vajadustele vastav. Trikk on selles, et me defineerime klassi, mis tuletab objektile Erand , siis kasutame raise-avaldust, et tõsta meie erandiklassi.
Oletame, et tahame kontrollida kasutaja sisendit ja veenduda, et sisendväärtus ei ole negatiivne(sanity check). Loomulikult võiksime tõsta Pythoni erandi ValueError, kuid me tahame viga kohandada, andes sellele konkreetse ja iseenesestmõistetava nime, näiteks InputIsNegativeError . Kuid see erand ei ole Pythoni sisseehitatud erand.
Nii et kõigepealt loome oma baasklassi, mis tuletatakse Exceptionist.
class CustomError(Exception): "Baasklassi erand kõigi selle mooduli erandite jaoks" pass
Seejärel loome oma erandiklassi, mis pärib baasklassi ja käsitleb meie spetsiifilist viga.
class InputIsNegativeError(CustomError): """Raised when User entered a negative value"" pass
Katsetame seda
try: value = int(input()) if value <0: raise InputIsNegativeError # Raise exception if value is negative except InputIsNegativeError: # catch and handle exception print("Sisendväärtus ei tohiks olla negatiivne")
Ülaltoodud kood küsib kasutaja sisendit ja kontrollib, kas see on negatiivne. Kui see on tõene, tekitab see meie kohandatud erandi InputIsNegativeError, mis hiljem püütakse kinni except-avalduses.
Allpool on täielik kood:
class CustomError(Exception): "Base class exception for all exceptions of this module" pass class InputIsNegativeError(CustomError): """Raised when User entered 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 exceptionprint("Sisendväärtus ei tohiks olla negatiivne")
Kui sisendväärtus on negatiivne arv, näiteks -1, siis on meil väljund:
Vaadake Pythoni dokumenti Pythoni kohandatud erandite kohta lisateavet.
Korduma kippuvad küsimused
K #1) Kuidas Python käitleb erandit?
Vastus: Python käsitleb erandeid kasutades try-except avaldis Koodi, mis võib tekitada erandi, paigutatakse ja täidetakse koodis. prooviblokk samas kui välja arvatud plokk sisaldab koodi, mis käitleb erandeid, kui need tekivad.
K #2) Mis on Pythonis erandi tekitamine?
Vastus: Iga kord, kui Pythoni interpretaator puutub kokku vigase koodiga, tekitab ta erandi, mis on Pythoni enda viis öelda meile, et midagi ootamatut juhtus. Me võime ka tahtlikult tekitada erandeid, kasutades selleks funktsiooni raise avaldus .
K #3) Kuidas käitleb Python mitmeid erandeid?
Vastus: Python käsitleb mitut erandit, kasutades kas ühte except-blokki või mitut except-blokki.
Ühe ploki puhul edastatakse erandid tuplina: välja arvatud (Exception1, Exception2,...,ExceptionN) ja Python kontrollib vastavust paremalt vasakule. Sel juhul tehakse iga erandi puhul sama tegevus.
Teine võimalus kõigi erandite püüdmiseks on jätta erandite nimi välja pärast võtmesõna except.
except: # käsitsege siin kõiki erandeid
Teine võimalus on kasutada iga erandi jaoks except-blokki:
except Exception1: # kood Exception1 käsitlemiseks läheb siia except Exception2: # kood Exception2 käsitlemiseks läheb siia except ExceptionN: # kood ExceptionN käsitlemiseks läheb siia
Nii saate iga erandi puhul võtta eraldi meetmeid.
K #4) Miks on Pythonis oluline erandite käsitlemine?
Vastus: Erandite käsitlemise eelis Pythonis on see, et me saame luua töökindlaid, puhtaid ja vigadeta rakendusi. Me ei taha, et meie tootmiskood mõne vea tõttu kokku kukub, seega käsitleme vigu ja hoiame oma rakendust töökorras.
K #5) Kuidas Pythonis erandeid ignoreerida?
Vastus: Erandi ignoreerimiseks Pythonis kasutage käsku pass võtmesõna except plokis. Oletame, et tahame ignoreerida ValueError erandit. Teeme seda nii:
except ValueError: pass
Kui te ei tea, mida teete, on halb tava ignoreerida erandeid. Teavitage kasutajat vähemalt kõikidest võimalikest vigadest.
Kokkuvõte
Selles õpetuses käsitlesime: Python Exceptions, Traceback; kuidas käsitleda erandeid koos Proovi / Välja arvatud / Muidu / Lõpuks plokid, kuidas Tõsta Erandid ja lõpuks, kuidas luua oma kohandatud erandeid.
Aitäh lugemise eest!