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.
  1. 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.
  2. 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 .
  3. print("Result is " + str(number_1/number_2)) is simply the line of code where the error happened.
  4. 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. Exceptions in Python: Managing Failures with Try-Except

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=108641&_unique_id=63e92adfc1b4e

Comments

Popular posts from this blog

Need an Hosting for Wordpress? The Best WordPress Hosting Sites Providers in 2022

Need an Hosting for Wordpress? The Best WordPress Hosting Sites Providers in 2022

Getting Started with Open Shortest Path First (OSPF)