Kuidas käsitleda Pythoni erandeid: üksikasjalik visuaalne sissejuhatus

Tere tulemast! Sellest artiklist saate teada, kuidas Pythonis erandeid käsitleda.

Eelkõige käsitleme järgmist:

  • Erandid
  • Erandite käsitlemise eesmärk
  • Prooviklausel
  • Välja arvatud klausel
  • Teine klausel
  • Viimane klausel
  • Kuidas erandeid tekitada

Oled sa valmis? Alustagem! ?

1️⃣ Sissejuhatus eranditesse

Alustame eranditega:

  • Mis need on?
  • Miks need asjakohased on?
  • Miks peaksite nendega hakkama saama?

Vastavalt Pythoni dokumentatsioonile:

Täitmisel tuvastatud vigu nimetatakse eranditeks ja need ei ole tingimusteta surmavad.

Erandid tekivad siis, kui programm leiab täitmisel viga. Need häirivad programmi tavapärast voogu ja lõpetavad selle tavaliselt järsult. Selle vältimiseks võite need kinni püüda ja asjakohaselt käsitseda.

Tõenäoliselt olete neid näinud oma programmeerimisprojektide ajal.

Kui olete kunagi proovinud Pythonis jagada nulliga, peate kindlasti nägema seda tõrketeadet:

>>> a = 5/0 Traceback (most recent call last): File "", line 1, in  a = 5/0 ZeroDivisionError: division by zero

Kui proovisite indeksi kehtetu indeksiga indekseerida, saate kindlasti selle tõrketeate:

>>> a = "Hello, World" >>> a[456] Traceback (most recent call last): File "", line 1, in  a[456] IndexError: string index out of range

Need on näited eranditest.

? Levinud erandid

Erandeid on palju erinevaid ja neid kõiki käsitletakse konkreetsetes olukordades. Mõned erandid, mida projektides töötades tõenäoliselt näete, on:

  • IndexError - tõstetakse üles, kui proovite loendit, dupletti või stringi üle lubatud piiride indekseerida. Näiteks:
>>> num = [1, 2, 6, 5] >>> num[56546546] Traceback (most recent call last): File "", line 1, in  num[56546546] IndexError: list index out of range
  • KeyError - tõstatub, kui proovite juurde pääseda võtme väärtusele, mida sõnastikus pole. Näiteks:
>>> students = {"Nora": 15, "Gino": 30} >>> students["Lisa"] Traceback (most recent call last): File "", line 1, in  students["Lisa"] KeyError: 'Lisa'
  • NameError - tõstetakse üles siis, kui koodis viidatud nime pole olemas. Näiteks:
>>> a = b Traceback (most recent call last): File "", line 1, in  a = b NameError: name 'b' is not defined
  • TypeError - tõstetakse üles, kui operatsioon või funktsioon rakendatakse sobimatut tüüpi objektile. Näiteks:
>>> (5, 6, 7) * (1, 2, 3) Traceback (most recent call last): File "", line 1, in  (5, 6, 7) * (1, 2, 3) TypeError: can't multiply sequence by non-int of type 'tuple'
  • ZeroDivisionError - tõstetakse üles, kui proovite jagada nulliga.
>>> a = 5/0 Traceback (most recent call last): File "", line 1, in  a = 5/0 ZeroDivisionError: division by zero

? Nõuanded: Lisateabe saamiseks muud tüüpi sisseehitatud erandite kohta lugege seda artiklit Pythoni dokumentatsioonis.

? Erandi anatoomia

Olen kindel, et olete kindlasti nendes veateadetes märganud üldist mustrit. Jaotame nende üldise struktuuri tükkhaaval:

Esiteks leiame selle rea (vt allpool). Jälgimise on põhimõtteliselt nimekirja üksikasjalikult funktsioon nõuab, et tehti enne välja arvatud tõsteti.

Jälgimine aitab silumisprotsessi ajal, sest saate analüüsida funktsioonikutsete järjestust, mille tulemuseks oli erand:

Traceback (most recent call last):

Seejärel näeme seda rida (vt allpool) koos faili tee ja reaga, mis erandi tõi. Sel juhul oli tee Pythoni kest, kuna näide käivitati otse IDLE-is.

File "", line 1, in  a - 5/0

? Nõuanne: kui erandi esile tõstnud rida kuulub funktsioonile, asendatakse funktsiooni nimega.

Lõpuks näeme kirjeldavat teadet, milles kirjeldatakse üksikasjalikult erandi tüüpi ja antakse lisateavet, mis aitab meil koodi siluda:

NameError: name 'a' is not defined

2️⃣ Erandite käsitlemine: eesmärk ja kontekst

Võite küsida: miks ma tahaksin käsitleda erandeid? Miks on see minu jaoks kasulik? Erandite käitlemisega saate pakkuda alternatiivse käivitamisvoo, et vältida programmi ootamatut krahhi.

? Näide: kasutaja sisend

Kujutage ette, mis juhtuks, kui teie programmiga töötav kasutaja sisestaks vale sisendi. See tooks erandi, kuna protsessi käigus tehti kehtetu toiming.

Kui teie programm ei tööta sellega õigesti, krahhib see ootamatult ja kasutaja saab teie tootega väga pettumust valmistava kogemuse.

Kuid kui te erandiga tegelete, saate pakkuda alternatiivi kasutaja kogemuse parandamiseks.

Perhaps you could display a descriptive message asking the user to enter a valid input, or you could provide a default value for the input. Depending on the context, you can choose what to do when this happens, and this is the magic of error handling. It can save the day when unexpected things happen. ⭐️

? What Happens Behind the Scenes?

Basically, when we handle an exception, we are telling the program what to do if the exception is raised. In that case, the "alternative" flow of execution will come to the rescue. If no exceptions are raised, the code will run as expected.

3️⃣ Time to Code: The try ... except Statement

Now that you know what exceptions are and why you should we handle them, we will start diving into the built-in tools that the Python languages offers for this purpose.

First, we have the most basic statement: try ... except.

Let's illustrate this with a simple example. We have this small program that asks the user to enter the name of a student to display his/her age:

students = {"Nora": 15, "Gino": 30} def print_student_age(): name = input("Please enter the name of the student: ") print(students[name]) print_student_age()

Notice how we are not validating user input at the moment, so the user might enter invalid values (names that are not in the dictionary) and the consequences would be catastrophic because the program would crash if a KeyError is raised:

# User Input Please enter the name of the student: "Daniel" # Error Message Traceback (most recent call last): File "", line 15, in  print_student_age() File "", line 13, in print_student_age print(students[name]) KeyError: '"Daniel"'

? Syntax

We can handle this nicely using try ... except. This is the basic syntax:

In our example, we would add the try ... except statement within the function. Let's break this down piece by piece:

students = {"Nora": 15, "Gino": 30} def print_student_age(): while True: try: name = input("Please enter the name of the student: ") print(students[name]) break except: print("This name is not registered") print_student_age()

If we "zoom in", we see the try ... except statement:

try: name = input("Please enter the name of the student: ") print(students[name]) break except: print("This name is not registered")
  • When the function is called, the try clause will run. If no exceptions are raised, the program will run as expected.
  • But if an exception is raised in the try clause, the flow of execution will immediately jump to the except clause to handle the exception.

? Note: This code is contained within a while loop to continue asking for user input if the value is invalid. This is an example:

Please enter the name of the student: "Lulu" This name is not registered Please enter the name of the student: 

This is great, right? Now we can continue asking for user input if the value is invalid.

At the moment, we are handling all possible exceptions with the same except clause. But what if we only want to handle a specific type of exception? Let's see how we could do this.

? Catching Specific Exceptions

Since not all types of exceptions are handled in the same way, we can specify which exceptions we would like to handle with this syntax:

This is an example. We are handling the ZeroDivisionError exception in case the user enters zero as the denominator:

def divide_integers(): while True: try: a = int(input("Please enter the numerator: ")) b = int(input("Please enter the denominator: ")) print(a / b) except ZeroDivisionError: print("Please enter a valid denominator.") divide_integers()

See oleks tulemus:

# First iteration Please enter the numerator: 5 Please enter the denominator: 0 Please enter a valid denominator. # Second iteration Please enter the numerator: 5 Please enter the denominator: 2 2.5

Me käitume sellega õigesti. Aga ... kui tõstatatakse teist tüüpi erandeid, ei hakka programm seda graatsiliselt käsitsema.

Siin on näide ValueErrorist, kuna üks väärtustest on ujuk, mitte int:

Please enter the numerator: 5 Please enter the denominator: 0.5 Traceback (most recent call last): File "", line 53, in  divide_integers() File "", line 47, in divide_integers b = int(input("Please enter the denominator: ")) ValueError: invalid literal for int() with base 10: '0.5'

Saame kohandada, kuidas erinevat tüüpi erandeid käsitleme.

? Mitu välja arvatud klauslit

Selleks peame lisama mitu exceptklauslit, et erinevat tüüpi erandeid erinevalt käsitleda.

Vastavalt Pythoni dokumentatsioonile:

Proovilauses võib olla mitu erandit, välja arvatud klausel , et määrata käitlejad erinevate erandite jaoks. Hukatakse maksimaalselt üks käitleja .

Selles näites on meil kaks lauset. Üks neist tegeleb ZeroDivisionErroriga ja teine ​​ValueErroriga, mis on kahte tüüpi erandeid, mida võiks selles prooviplokis tõstatada.

def divide_integers(): while True: try: a = int(input("Please enter the numerator: ")) b = int(input("Please enter the denominator: ")) print(a / b) except ZeroDivisionError: print("Please enter a valid denominator.") except ValueError: print("Both values have to be integers.") divide_integers() 

? Nõuanne. Nende sobivaks käitlemiseks peate määrama, millist tüüpi erandeid võib prooviplokis tõstatada.

? mitu erandit, üks välja arvatud klausel

Võite valida ka erinevat tüüpi erandite käsitsemise sama välja arvatud klausliga.

Vastavalt Pythoni dokumentatsioonile:

Välja arvatud klausel võib mitu erandit nimetada sulgudena.

See on näide, kus tabame kaks erandit (ZeroDivisionError ja ValueError) sama exceptklausliga:

def divide_integers(): while True: try: a = int(input("Please enter the numerator: ")) b = int(input("Please enter the denominator: ")) print(a / b) except (ZeroDivisionError, ValueError): print("Please enter valid integers.") divide_integers()

Kahe eranditüübi puhul oleks väljund sama, kuna neid käsitletakse sama, välja arvatud klausel:

Please enter the numerator: 5 Please enter the denominator: 0 Please enter valid integers.
Please enter the numerator: 0.5 Please enter valid integers. Please enter the numerator: 

? Proovilauses kutsutud funktsioonide poolt tõstatatud erandite käsitlemine

Erandite käsitlemise huvitav aspekt on see, et kui funktsioonis, mida varem kutsuti mõne muu funktsiooni try klauslis, tõsteti erand ja funktsioon ise sellega ei tegele, siis helistaja tegeleb sellega, kui on olemas sobiv välja arvatud klausel.

Vastavalt Pythoni dokumentatsioonile:

Erandite käitlejad ei käsitle erandeid ainult juhul, kui need ilmnevad kohe prooviklauslis, vaid ka siis, kui need esinevad funktsioonides, mida kutsutakse (isegi kaudselt) prooviklauslis.

Vaatame näite selle illustreerimiseks:

def f(i): try: g(i) except IndexError: print("Please enter a valid index") def g(i): a = "Hello" return a[i] f(50)

We have the f function and the g function. f calls g in the try clause. With the argument 50, g will raise an IndexError because the index 50 is not valid for the string a.

But g itself doesn't handle the exception. Notice how there is no try ... except statement in the g function. Since it doesn't handle the exception, it "sends" it to f to see if it can handle it, as you can see in the diagram below:

Since f does know how to handle an IndexError, the situation is handled gracefully and this is the output:

Please enter a valid index

? Note: If f had not handled the exception, the program would have ended abruptly with the default error message for an IndexError.

? juurdepääs erandite üksikasjadele

Erandid on Pythoni objektid, nii et saate määrata muutujale tõstatatud erandi. Nii saate printida erandi vaikekirjelduse ja pääseda juurde selle argumentidele.

Vastavalt Pythoni dokumentatsioonile:

Välja arvatud klausel võib erandi nime järel määrata muutuja . Muutuja on seotud erandi eksemplariga, mille argumendid on salvestatud instantsile.args.

Siin on näide (vt allpool), kui määrasime ZeroDivisionErrormuutujale eksemplari e. Seejärel saame kasutada seda muutujat erandklauslis, et pääseda juurde erandi tüübile, selle sõnumile ja argumentidele.

def divide_integers(): while True: try: a = int(input("Please enter the numerator: ")) b = int(input("Please enter the denominator: ")) print(a / b) # Here we assign the exception to the variable e except ZeroDivisionError as e: print(type(e)) print(e) print(e.args) divide_integers()

Vastav väljund oleks:

Please enter the numerator: 5 Please enter the denominator: 0 # Type  # Message division by zero # Args ('division by zero',)

? Nõuanne. Kui olete Pythoni dokumentatsiooni kohaselt erimeetoditega tuttav: "mugavuse huvides määratleb erandi eksemplar __str__()nii, et argumente saab otse printida ilma viiteta .args".

4️⃣ Lisame nüüd: klausli "muu"

elseKlausel on vabatahtlik, kuid see on suurepärane vahend, sest see võimaldab meil täita kood, mis peaks ainult joosta, kui mingeid erandeid tekkisid proovida klausel.

Vastavalt Pythoni dokumentatsioonile:

Lausel tryexcepton valikuline muu klausel , mis olemasolul peab järgima kõiki, välja arvatud klausleid. See on kasulik koodi jaoks, mis tuleb käivitada, kui prooviklausel ei tekita erandit.

Siin on näide elseklausli kasutamisest:

def divide_integers(): while True: try: a = int(input("Please enter the numerator: ")) b = int(input("Please enter the denominator: ")) result = a / b except (ZeroDivisionError, ValueError): print("Please enter valid integers. The denominator can't be zero") else: print(result) divide_integers()

Kui erandeid ei esitata, prinditakse tulemus:

Please enter the numerator: 5 Please enter the denominator: 5 1.0

Kuid kui tehakse erand, siis tulemust ei prindita:

Please enter the numerator: 5 Please enter the denominator: 0 Please enter valid integers. The denominator can't be zero

? Nõuanne: vastavalt Pythoni dokumentatsioonile:

Kasutamine elseklausel on parem kui lisades täiendavat koodi tryklausel, sest see väldib kogemata püüdmine erand, mis ei olnud tõstatatud kood on kaitstud try... exceptavaldusega.

5️⃣ "Lõpuks" klausel

Viimane klausel on selle jada viimane klausel. See ei ole kohustuslik , kuid kui lisate selle, peab see olema järjestuse viimane lause. finallyKlausel on alati täidetud, isegi kui erand tõstatati proovida klausel.  

Vastavalt Pythoni dokumentatsioonile:

Kui on olemas finallyklausel, täidetakse finallyklauslit viimase ülesandena enne trylause lõpetamist. finallyKlausel jookseb kas tryavaldus toodab erand.

Viimast klauslit kasutatakse tavaliselt "puhastustoimingute" tegemiseks, mis tuleks alati lõpule viia. Näiteks kui töötame prooviklauslis oleva failiga, peame faili alati sulgema, isegi kui andmetega töötamisel tehti erand.

Siin on näide viimasest klauslist:

def divide_integers(): while True: try: a = int(input("Please enter the numerator: ")) b = int(input("Please enter the denominator: ")) result = a / b except (ZeroDivisionError, ValueError): print("Please enter valid integers. The denominator can't be zero") else: print(result) finally: print("Inside the finally clause") divide_integers()

See on väljund, kui erandeid ei tehtud:

Please enter the numerator: 5 Please enter the denominator: 5 1.0 Inside the finally clause

See on väljund, kui tehti erand:

Please enter the numerator: 5 Please enter the denominator: 0 Please enter valid integers. The denominator can't be zero Inside the finally clause

Pange tähele, kuidas finallyklausel alati töötab.

❗️Important: remember that the else clause and the finally clause are optional, but if you decide to include both, the finally clause has to be the last clause in the sequence.

6️⃣ Raising Exceptions

Now that you know how to handle exceptions in Python, I would like to share with you this helpful tip: you can also choose when to raise exceptions in your code.

This can be helpful for certain scenarios. Let's see how you can do this:

This line will raise a ValueError with a custom message.

Here we have an example (see below) of a function that prints the value of the items of a list or tuple, or the characters in a string. But you decided that you want the list, tuple, or string to be of length 5. You start the function with an if statement that checks if the length of the argument data is 5. If it isn't, a ValueError exception is raised:

def print_five_items(data): if len(data) != 5: raise ValueError("The argument must have five elements") for item in data: print(item) print_five_items([5, 2])

The output would be:

Traceback (most recent call last): File "", line 122, in  print_five_items([5, 2]) File "", line 117, in print_five_items raise ValueError("The argument must have five elements") ValueError: The argument must have five elements

Notice how the last line displays the descriptive message:

ValueError: The argument must have five elements

You can then choose how to handle the exception with a try ... except statement. You could add an else clause and/or a finally clause. You can customize it to fit your needs.

? Helpful Resources

  • Exceptions
  • Handling Exceptions
  • Defining Clean-up Actions

Loodan, et teile meeldis minu artiklit lugeda ja see oli teile kasulik. Nüüd on teil Pythonis erandite käsitsemiseks vajalikud tööriistad ja saate neid Pythoni koodi kirjutamisel enda huvides kasutada. ? Vaadake minu veebikursusi. Saate mind jälgida Twitteris.

⭐️ Võite nautida minu teisi freeCodeCampi / uudiseid:

  • @Property sisekujundaja Pythonis: selle kasutamise juhtumid, eelised ja süntaks
  • Andmestruktuurid 101: Graafikud - visuaalne sissejuhatus algajatele
  • Andmestruktuurid 101: massiivid - visuaalne sissejuhatus algajatele