Exceptions and errors are undesirable events in any program which may lead to termination of the program execution prematurely. In python,We use try-except error handling to handle the exceptions with python try except and finally blocks. In this article, we will look at a few reasons to use exception handling in python using different examples.  So Lets Start!

### We can handle errors which cannot be predicted by the programmer

In real world applications, there are many use cases and constraints on the variables used in the programs. Every time it is not possible for the programmer to check all the constraints and they may miss some cases which may lead to error in the program while execution. In these cases, we can use try-except error handling to handle the unexpected errors and recover the program if needed.

For example, Suppose the user presses ctrl+c or del key to interrupt the program in between the execution, then the program abruptly terminates because KeyBoardInterrupt error occurs. We can handle the error using try-except error handling as follows and show custom message before terminating the program or we can execute other statements to save the state of the program in the file system. In this way we can avoid loss of data written to files by the programs if the user accidently presses keys to interrupt the program.

try:
dividend=int(input())
divisor=int(input())
print("Dividend is:",end=" ")
print(dividend)
print("Divisor is:",end=" ")
print(divisor)
quotient=dividend/divisor
print("Quotient is:",end=" ")
print(quotient)
except (KeyboardInterrupt):
print("Operation has been cancelled by the user")

### Using try-except error handling, we can Handling Runtime Exceptions

Even though the program handles all the constraints and the program is syntactically correct, there may be situations where an error which doesn’t depend on logic implemented in the program may occur during the execution of the program. For example, If a file is not found during a python read file operation, the program may run into error and may terminate the execution prematurely. In such cases, the program should behave more robustly and handle the situation in a smooth way. This can be done using try-except error handling in python. . Other runtime errors  like ZeroDivisionErrorKeyErrorNameErrorIndexError etc can also be handled  using try-except error handling in an efficient way.

For example, the following program is written to divide a number by other but when divisor becomes zero, it will cause ZeroDivisionError and the program will terminate giving the output as follows.

dividend=10
divisor=0
print("Dividend is:",end=" ")
print(dividend)
print("Divisor is:",end=" ")
print(divisor)
quotient=dividend/divisor
print("Quotient is:",end=" ")
print(quotient)

Output:

Dividend is: 10
Divisor is: 0
Traceback (most recent call last):

File "<ipython-input-17-f6b48848354a>", line 1, in <module>

File "/usr/lib/python3/dist-packages/spyder_kernels/customize/spydercustomize.py", line 827, in runfile
execfile(filename, namespace)

File "/usr/lib/python3/dist-packages/spyder_kernels/customize/spydercustomize.py", line 110, in execfile
exec(compile(f.read(), filename, 'exec'), namespace)

File "/home/aditya1117/untitled0.py", line 9, in <module>
quotient=dividend/divisor

ZeroDivisionError: division by zero

We can avoid the premature termination of the program by enclosing the division operation in a try-except block so that when ZeroDivisionError occurs, It will automatically be handled by the code in the except block.This can be seen in the following program.

dividend=10
divisor=0
print("Dividend is:",end=" ")
print(dividend)
print("Divisor is:",end=" ")
print(divisor)
try:
quotient=dividend/divisor
print("Quotient is:",end=" ")
print(quotient)
except (ZeroDivisionError):
print("Divisor Cannot be zero")

Output:

Dividend is: 10
Divisor is: 0
Divisor Cannot be zero

### We can separate error handling code from business logic using try-except error handling.

By using try-except error handling, we can easily separate code which handles error from the code which implements the logic. Using try except error handling makes the code more readable as business logic and error handling code are entirely separated.

For example, Suppose we want to determine the birth year of a person and we have to put a check that age should not be negative. We will do this using if-else conditional statements as following.

age= -10
print("Age is:")
print(age)
if age<0:
print("Input Correct age.")
else:
yearOfBirth= 2021-age
print("Year of Birth is:")
print(yearOfBirth)

Output:

Age is:
-10
Input Correct age.

In the above program, we are checking as well as handling the case when age is negative in the same code block. We can divide the condition checking into one block and error handling into another block using try-except error handling as follows.

try:
age= -10
print("Age is:")
print(age)
if age<0:
raise ValueError
yearOfBirth= 2021-age
print("Year of Birth is:")
print(yearOfBirth)
except ValueError:
print("Input Correct age.")

Output:

Age is:
-10
Input Correct age.

Here, we can see that we have checked for the non negativity for the age in try block and ValueError is raised when age is negative. ValueError is then handled by the code in except block to print the message. Thus we have separated condition checking and error handling code into different blocks which will lead to better readability of the source code.

### We can propagate errors up the call stack using try-except error handling.

In python, If a function is called by another function, it can propagate errors to the caller function in the same way the called function returns any value. This mechanism can be used to propagate errors to any number of functions in the function stack and thus it gives us freedom to implement all the exception handling code at one place instead of implementing it in every function. We can handle all the errors using try-except error handling in the main function itself and raise exceptions or errors in the called functions. Whenever any exception will be raised in any of the called functions, it will be propagated to the main function where it should be handled with proper error handling code.

For example, In the code below, ValueError is raised in function a() but code to handle ValueError is written in function b(). When the function b() calls function a()a() propagates the error to function b() and then the error is handled and message is printed.

def a():
print("PythonForBeginners in function a")
raise ValueError
def b():
try:
print("PythonForBeginners in function b")
a()
except Exception as e:
print("Caught error from called function in function b")
#call function b
b()

Output:

PythonForBeginners in function b
PythonForBeginners in function a
Caught error from called function in function b

In the above process,  whenever an exception occurs in any function, the python interpreter searches the function call stack backwards to check if the error handling code for the particular error has been implemented in any caller function or not in case the error is not handled in the function itself. If the current  function at any stage doesn’t have the proper error handling code, the error is propagated to the caller function of the current function until proper error handling code is found or we reach the main function.

### Conclusion

In this article, we have seen how to perform different tasks using try-except error handling in python using different examples. We have also briefly read about how errors are propagated in a function call stack and how it can be useful for writing modular source code. Stay tuned for more informative articles.