Concepts and Theory
In Python, we can encounter two types of errors in our program. One is Syntax error and other is Runtime error.
The syntax error do not let the program (or a part of the program) to even try for execution because it is syntactically incorrect and the related errors will be displayed as an output of the interpreter. Lets see an example.
class Calc: def Inverse(self, num) return 1/num cal = Calc() cal.Inverse(0)
Running this program will give you the following error
File "d:\Users\Satya Jugran\Python 3 Tutorial\exceptionHandling.py", line 2 def Inverse(num) ^ SyntaxError: invalid syntax
The above error occurred because a colon is missing after the function definition. In the last line it is clearly stating that it is a SyntaxError. Now lets add the colon as you can see in the following code.
class Calc: def Inverse(self, num): return 1/num cal = Calc() cal.Inverse(0)
Now if you try running this code again, you will get the following error.
Traceback (most recent call last): File "d:\Users\Satya Jugran\Python 3 Tutorial\exceptionHandling.py", line 6, in <module> cal.Inverse(0) File "d:\Users\Satya Jugran\Python 3 Tutorial\exceptionHandling.py", line 3, in Inverse return 1/num ZeroDivisionError: division by zero
In the above code inside the function Inverse, since 1 can not be divided by 0, an error is thrown by the interpreter even though the code is syntactically correct.
When our program is syntactically correct but still some error gets generated at runtime, such errors are called Exceptions.
For syntactical error, the interpreter will tell you immediately what the error is. However, this is not the case with Exceptions. This is because the exception might occur due to several other reasons which may or may not encounter in our code. For example, if we are taking an integer input from the user and we are passing that into our Inverse function (in above code example), we are not sure weather the user will input a valid number or 0.
Handling of such exceptions is called Exception Handling
Python provides us with try and except keywords to handle exception. Lets modify our previous example to include exception handling.
class Calc: def Inverse(self, num): return 1/num try: cal = Calc() cal.Inverse(0) except Exception as err: print("An exception has occurred")
In the above example, we have the wrapped the exception generating code between try and except keywords. Any code between try and except keywords will be tried for execution by the interpreter. If the code generates an exception, the control will be passed to the except code block. The exception message will be captured in the instance (err variable) of the Python's general exception class (Exception) and the except block code will be executed.
The difference in this case is that the interpreter will continue to execute the rest of the code because exception was gracefully handled. However, the case where we were not handling exception, the interpreter stops at that line and exit from the program.
Handling specific exceptions
In the previous section we have used Exception class. This is base exception class from which the specific exception classes are derived. Lets modify previous example to handle specific exception.
class Calc: def Inverse(self, num): return 1/num try: cal = Calc() cal.Inverse(0) except ZeroDivisionError as zeroDivideError: print("A zero division exception has occurred. Exception message -", zeroDivideError) except Exception as err: print("An exception has occurred -", err)
It will show the following output
A zero division exception has occurred. Exception message - division by zero
We can make an except clauses chain (like above). The except clause which will be executed is the one which will match the exception class starting from top. In this example the exception was of type ZeroDivisionError exception. We have now modified the example just by changing Calc() to Calc("")
try: cal = Calc("") cal.Inverse(0) except ZeroDivisionError as zeroDivideError: print("A zero division exception has occurred. Exception message -", zeroDivideError) except Exception as err: print("An exception has occurred -", err)
Executing the above example will give the following error.
An exception has occurred - Calc() takes no arguments
This time the second except clause is executed because the exception class that matched the error this time is Exception
We can raise exceptions any point in our code where we don't want a particular scenario to happen. Lets see the below example.
class Student: def setAge(self, age): if(age < 18): raise Exception("Age can not be less than 18") else: self.age = age alice = Student() try: alice.setAge(15) except Exception as error: print("An exception has occurred -", error) # Output -> An exception has occurred - age can not be less than 18
In the above example, we have setAge method of Student class which takes age as a parameter. Inside the method, we are checking a scenario that if age is less than 18 then raise an exception. In this way, we can raise exceptions as per our requirement.
The else and finally keywords
Python provides us with two more keywords that can be used in exception handling. These are else and finally.
The else keyword is used to execute a block of statement when there is no exception. Lets see in the below example how else keyword is used.
bob = Student() try: bob.setAge(20) except Exception as error: print("An exception has occurred -", error) else print("Age was assigned successfully!")
So the output of this code would be
Age was assigned successfully!
The block of code under finally keyword is always executed irrespective of whether the exception occurred or not. Lets add a finally block in our code.
bob = Student() try: bob.setAge(20) except Exception as error: print("An exception has occurred -", error) else: print("Age was assigned successfully!") finally: print("Finally block - This always gets executed")
The output will be
Age was assigned successfully! This always gets executed
User Defined Exceptions
In the previous examples, we simply raised exception with predefined exception classes. However, we can create our own exception/error classes.
This can be required in the following cases
- To process something in case of an exception like clean up of resource.
- To create a common exception handler for our entire application that provide error specific error codes as well.
- It is generally a practice of clean coding.
class SalaryError(Exception): def __init__(self, arg): self.arg = arg class Employee: def setSalary(self, salary): if(salary < 0): raise SalaryError("Salary can't be less than 0") else: self.salary = salary try: chris = Employee() chris.setSalary(-100) except SalaryError as error: print("Salary exception occurred - ", error)
In the above example
- We have created a SalaryError class and inherit the same with Exception class. Note that as a practice, user defined exceptions are generally suffixed with word Error
- We have created an Employee class. In this class we are raising error in case the salary is less than 0.
- In our main program, we made an instance of Employee class named - chris and we set the salary of chris to -100. Since we have wrapped our main program under try catch, the generated exception gets caught and the error message is printed.
Python jupyter notebook for code examples
Access Jupyter notebook for this chapter here:
Jupyter Notebook - Exception Handling