Exceptions in Python: Managing Failures with Try-Except
You may now create intricate scripts by beginning with functions and progressing to classes. These scripts will handle complex data, and you must ensure that they do not fail. In fact, a data-driven script must know what to do when the data is inaccurate. We cannot just assume that the user will submit correct information. While your knowledge may already help you avoid difficulties, we will learn today how to make your code error-tolerant. To do this, we will make use of a fantastic feature: Python Exceptions, or the famous
try ... except
construct.
Table of Contents about Exceptions in Python: Managing Failures with Try-Except
Here’s what we are going to cover today:
Where do errors come from?[ps2id id='Where do errors come from?' target=''/]
When I first started coding, I couldn't understand why a code might generate an error. In reality, the reasons of a mistake are many, and many of them are obvious. Of course, a code may have flaws that result in unanticipated problems, but this is not the only option. In actuality, the most common cause of a mistake is when the computer encounters unexpected data.
Errors may have a variety of reasons.
The classical example is the division by zero, which is not possible. No number, multiplied by 0, can generate a number different than zero: it is simply impossible. Thus, imagine we have the following code.
number_1 = int(input("Write the first number: "))
number_2 = int(input("Write the second number: "))
print("Result is " + str(number_1/number_2))
This works like a charm, as long as the user keeps giving us what we expect. Instead, if he gives us
0
as a second number, it will generate an error.
Looking at our first error
If we execute the code from the previous snippet, with zero as the second number, this will be the result.
C:\Users\aless\Desktop>python myscript.py
Write the first number: 18
Write the second number: 0
Traceback (most recent call last):
File "myscript.py", line 4, in
print("Result is " + str(number_1/number_2))
ZeroDivisionError: division by zero
Don’t get scared by this message, it is extremely simple to understand. You can see the error message has four lines. We can analyze them in sequence.
Traceback (most recent call last):
is simply indicating we have an error. Since we have an error at the root of our code, we will see just these four lines. But imagine you have a function, that calls a function, that calls another function and so on. You will have several lines of messages to identify when the error happened. We will see that later.File "myscript.py", line 4, in
tells the file that generated the error and the module. Since we haven’t defined any module name, the module is replaced by the string
.print("Result is " + str(number_1/number_2))
is simply the line of code where the error happened.ZeroDivisionError: division by zero
tells you which type of error we encountered (ZeroDivisionError
) and an explicative message (division by zero
).
An error nested in a function
It is very unlikely that you will see errors at the root of your scope. Most likely, errors will happen inside a function or class method that is probably called from other scopes as well. Here is the exact same code as above, but with several functions to show you the point.
def perform_division(num_1, num_2):
return num_1/num_2
def division_as_string(num_1, num_2):
return str(perform_division(num_1, num_2))
def get_division_results(num_1, num_2):
return "The result is: " + division_as_string(num_1, num_2)
number_1 = int(input("Write the first number: "))
number_2 = int(input("Write the second number: "))
print(get_division_results(number_1, number_2))
Here the error will be a little longer, but yet without additional complexity.
C:\Users\aless\Desktop>python myscript.py
Write the first number: 18
Write the second number: 0
Traceback (most recent call last):
File "myscript.py", line 13, in
print(get_division_results(number_1, number_2))
File "myscript.py", line 8, in get_division_results
return "The result is: " + division_as_string(num_1, num_2)
File "myscript.py", line 5, in division_as_string
return str(perform_division(num_1, num_2))
File "myscript.py", line 2, in perform_division
return num_1/num_2
ZeroDivisionError: division by zero
As we can see, the line indicating the file and the line indicating the piece of code generating the error are repeated. We start from the outer scope on top (line 13) to drill down until we reach the point where the error actually happened. This way you can quickly skim through your code. This is a lifesaver if you want to troubleshoot which function is passing parameters to the other in the wrong way.
Python exceptions to the rescue![ps2id id='Python exceptions to the rescue!' target=''/]
Preventing python exceptions
The errors you see are the python exceptions because the code is handling with something unexpected. You can write code that generates no exception, simply with
if
and else
. Thus, we can rewrite our code above to prevent the error.
def division(num_1, num_2):
if num_2 == 0:
return "Cannot divide by 0"
return num_1 / num_2
number_1 = int(input("Write the first number: "))
number_2 = int(input("Write the second number: "))
print("The result is: " + str(division(number_1, number_2)))
In case we attempt to give 0 as the second number, the script will recognize that before the error happens. This is a simple way to write code that deals with problems. It is completely okay, in fact, Google likes this coding style. However, this poses some serious limitations. Just continue reading to find out why.
Handling Python Exceptions
With the selection statement, you can only prevent exceptions. You cannot deal with them if they arise. Instead, to deal with them we have a special statement, the
try .. except
. This construct is simple: Python will attempt to execute the code in the try
block. In case an exception arises, it will switch to the except
block.
Try to change your
division
function to reflect the one below.
def division(num_1, num_2):
try:
return num_1 / num_2
except:
return "We had an error"
Every time the division is successful, the code inside except won’t be executed. Instead, if we put
0
in num_2
, Python will execute that code instead of performing the division.
Right now, you may think there is no advantage when comparing to exception prevention. However, exceptions raise to the outer scope until they find a
try ... except
that handles them. This means we can take care of this error even in a different point of the code. We can change our script like the one below.
def division(num_1, num_2):
return num_1 / num_2
number_1 = int(input("Write the first number: "))
number_2 = int(input("Write the second number: "))
try:
print("The result is: " + str(division(number_1, number_2)))
except:
print("Error while performing calculation")
The real advantage of handling Python Exceptions
Why on earth would you need to handle exceptions apart from where they happen? Haha, this is a fantastic question, and the solution comes with practice. In certain instances, you wish to have some control over the mistakes in the outside scope.
The reason for it is simple that managing exceptions is a key component of your code, not just something you have to perform afterwards. A popular example is when designing a module, a library, or anything that other developers will utilize. You want to provide people the option to respond the way they desire in case of mistake. Thus, you construct functions that do not handle exceptions or merely handle part of them. The same is true when you wish to construct these libraries for yourself. You want your library to be general, therefore handling certain exceptions inside of it may not be a smart idea.
Remember, our purpose is not to be entirely mute on mistakes. Errors may still arise, and it is OK to convey them to users (maybe in a more legible style) (maybe in a more readable format). Instead, our major purpose is to guarantee that the software handles problems in the proper manner, and for some of them, the appropriate approach involves showing them to the user. Think about it, in our previous example, we didn’t change the program. We simply notified the individual that he was giving a terrible
number_2
.
Handling multiple exceptions
In the
except
statement, you can define which exception you wish to handle with it. This way, you can attach multiple except
statements to the same try
, so that each deals with a specific exception. This code is an example of that.
def division(num_1, num_2):
return num_1 / num_2
try:
number_1 = int(input("Write the first number: "))
number_2 = int(input("Write the second number: "))
print("The result is: " + str(division(number_1, number_2)))
except ZeroDivisionError:
print("Cannot divide by zero!")
except ValueError:
print("Insert only integers!")
except:
print("Generic error...")
As you can see, the last statement does not specify any specific exception to handle. Thus, it will catch any error that is not
ZeroDivisionError
nor ValueError
. However, the Python standards recommend you to not use just except
. Instead, you should always provide the error you are going to handle.
What if some error is not handled? You should know what are all the possible errors your code may generate, and handle them correctly with individual statements.
If you want, you can condense multiple errors in a single statement with brackets, like
except(NameError, IndexError):
.
Finally
Each
try
must have at least one except
. However, you can also attach a finally
statement after all your except
statements. Python will execute the code here in any case, after the try
(and the except
, if it was triggered). You need this construct if, for example, you need to close files or similar stuff.
def division(num_1, num_2):
return num_1 / num_2
try:
number_1 = int(input("Write the first number: "))
number_2 = int(input("Write the second number: "))
print("The result is: " + str(division(number_1, number_2)))
except ZeroDivisionError:
print("Cannot divide by zero!")
except ValueError:
print("Insert only integers!")
except:
print("Generic error...")
finally:
print("Finished!")
Working on the exception
In the except statement, you might actually need to work on the exception you received. For example, you might want to store its error in a file. You can do that by assigning the exception received to a variable, with the keyword
as
.
...
except ValueError as e:
with open("errors.log", "a") as file:
file.write("Error #" + str(e.errno))
In this snippet, we write the error number to a file. We do that by leveraging the
errno
property of every error. We can also find the message in the strerror
property.
Creating Python Exceptions[ps2id id='Creating Python Exceptions' target=''/]
If you can handle Python Exceptions, it is only because someone else created them. The
ZeroDivisionError
is embedded inside the division operand (/
), for example. The ValueError
can be triggered by the int()
function, and so on. Like someone defined these exceptions, you can tell your code when to generate exceptions. Here we have a cool example.
def validate(day, month):
if day < 1:
raise ValueError("Day must be greater than 1")
if month < 1 or month > 12:
raise ValueError("Month must be between 1 and 12")
try:
if month == 1 or month == 3 or month == 5 or month == 7 or month == 8 or month == 10 or month == 12:
if day > 31:
raise ValueError
elif month == 2:
if day != 28 and day != 29:
raise ValueError
else:
if day > 30:
raise ValueError
except ValueError:
raise ValueError("Month " + str(month) + " cannot have " + str(day) + " days")
day = int(input("Day: "))
month = int(input("Month: "))
validate(day, month)
In this example, our script won’t output anything. Instead, it will only generate an error in case something is wrong. To generate an error, we use the keyword
raise
. The keyword is “raise” because errors are meant to be raised to the outer scope, and so on until they are handled. To that keyword, we need to add the name of the error and any parameters it requires. Remember that errors are just objects like others.
Explaining the good stuff
In our example, we start by checking the absolute validity of day and month. In case they are wrong, we quickly raise an error with a description (as the
ValueError
can accept that). However, the good stuff happens right after that.
In the second part of the function, we validate combination of month and day inside a
try
block. In case something is wrong, we simply raise a ValueError
without description. The try
block will then switch to the except
block, and handle that error. Here we generate another error, and we add the message. This way we don’t need to write the same message three times. Pretty neat.
Custom Python Exceptions
Now you can raise existing Python exceptions, but sometimes you want to define your own. This is an advanced topic, as you need to define a class that inherits from another, and we haven’t talked about that yet. However, for now, just know that it is possible to have your own error with your own name. Simply use this construct.
class ValidationError(Exception):
pass
As you can see, we simply define a class that gets its properties from the default
Exception
class. We name our class ValidationError, yet we don’t define anything and instead use the keyword pass
. Once you will learn about inheritance, you will also learn about defining additional properties. As a naming convention, include “Error” in the name.
Default Python Exceptions
Python has a lot of pre-built errors. Below the ones you must know.
IndexError
happens when you have problems with lists, like if you are trying to access an item that does not exist.NameError
is similar to the IndexError, but applies with dictionaries.ValueError
comes when something is not of the type it should be. Maybe it was a string but should have been an integer – something like that.EOFError
deals with the end of a file. If you are reading a file, it might be corrupted. Most likely, you missed some brackets in your python file and thus Python does not know when to close them.
Conclusion[ps2id id='Conclusion' target=''/]
Now that you understand python exceptions and how to handle them, you should be able to write bug-free programs. Don't worry if your code is still shaky at first; practice will make perfect. It's a good idea to put your code to the test using odd inputs. Try inserting some text where you want to see numbers, or vice versa. Insert spaces at the start or end of user inputs, or provide a file of a different type instead of the expected one.
If Our Method Resolve Your Problem Consider To Share This Post, You can help more People Facing This Problem and also, if you want, you can Subscribe at Our Youtube Channel as Well!
What do you think about Python Exceptions? What plans do you have for them? What was the most difficult component about them? Let me know in the comments!
https://www.techguruhub.net/exceptions-python-managing-failures/?feed_id=112579&_unique_id=64149c636790d
Comments
Post a Comment