If you are a coding veteran or write production code, you would have certainly come in the contact of those pesky errors. Being able to handle these errors makes you a good programmer, therefore it is very necessary for you to properly debug and handle those errors and bugs. In this tutorial, we will try to have an in-depth analysis of Exception handling in Python.
In case you missed our previous posts you can check them out from these links:
- Binary Tree Implementation in Python: In this tutorial, we implemented one of the most used data structure in Python.
- Top 5 data science libraries in Python: Top 5 most used data science libraries in python
What is Exception Handling?
In easy words, Exceptions are unexpected events that occur during the execution of a program.
An exception might result from a logical error or an unanticipated situation. In Python, exceptions (also known as errors) are objects that are raised (or thrown) by code that encounters an unexpected circumstance.
- The Python interpreter can also raise an exception should it encounter an unexpected condition, like running out of memory.
- A raised error may be caught by a surrounding context that “handles” the exception in an appropriate fashion.
- If uncaught, an exception causes the interpreter to stop executing the program and to report an appropriate message to the console.
In the next sections, we will examine the most common error types in Python, the mechanism for catching and handling errors that have been raised, and the syntax for raising errors from within user-defined blocks of code.
Common Exception classes in Python
|Exception||A base class for most error types|
|AttributeError||Raised by syntax object|
|EOFError||Raised if “end of file” reached for console or file output|
|IOError||Raised upon failure of I/O operation (e.g. opening file)|
|IndexError||Raised if index to sequence is out of bounds|
|KeyError||Raised if nonexistent key requested for set or dictionary|
|KeyboardInterrupt||Raised if user types ctrl-C while program is executing|
|NameError||Raised if nonexistent identifier used|
|StopIteration||Raised by next(iterator) if no element;|
|TypeError||Raised when wrong type of parameter is sent to a function|
|ValueError||Raised when the parameter has an |
|ZeroDivisionError||Raised when any division operator used with 0 as divisor|
Let’s see some examples for these exception classes in Python 3.6 Shell.
To open a Python Shell in your PC – Simply type IDLE in the Search bar and select the Python3.x + version of IDLE.
If you don’t have Python IDLE installed in your computer you can go through our Introduction to Python Tutorial to know how to install the same.
This class covers all kinds of exceptions.
>>> i = 'a' >>> z = int(i) Traceback (most recent call last): File "<pyshell#3>", line 1, in <module> z = int(i) ValueError: invalid literal for int() with base 10: 'a' >>> try: z = int(i) except Exception: print("An Error Occurred") An Error Occurred
Raised by syntax object.fun, if an object has no membered name fun
>>> class Car: color = 'red' >>> new_car = Car() >>> new_car.color 'red' >>> new_car.name Traceback (most recent call last): File "<pyshell#14>", line 1, in <module> new_car.name AttributeError: 'Car' object has no attribute 'name' >>> try: print(new_car.name) except AttributeError: print("attribute error occurred") attribute error occurred
This error generally occurs on iterable objects and is raised if index to sequence is out of bounds.
>>> txt = 'pyblog' >>> for i in range(10): print(txt[i]) p y b l o g Traceback (most recent call last): File "<pyshell#3>", line 2, in <module> print(txt[i]) IndexError: string index out of range >>> # Or simply >>> txt Traceback (most recent call last): File "<pyshell#5>", line 1, in <module> txt IndexError: string index out of range >>> try: print(txt) except IndexError: print('Index error occurred') Index error occurred
Other Error Classes
>>> print(unknown_var) Traceback (most recent call last): File "<pyshell#11>", line 1, in <module> print(unknown_var) NameError: name 'unknown_var' is not defined >>> print(1/0) Traceback (most recent call last): File "<pyshell#12>", line 1, in <module> print(1/0) ZeroDivisionError: division by zero >>> def divide(i,j): return i/j >>> divide('a','b') Traceback (most recent call last): File "<pyshell#17>", line 1, in <module> divide('a','b') File "<pyshell#16>", line 2, in divide return i/j TypeError: unsupported operand type(s) for /: 'str' and 'str'
In the previous examples you saw various errors and the exceptions to handle them. Let’s see more about the working of try/except clause in next section.
Process of Exception handling in Python
try statement works as follows.
- First, the try clause (the statement(s) between
keywords) is executed.
- If no exception occurs, the except clause is skipped and execution of
- If an exception occurs during
executionof the try clause, the rest of the clause is skipped. Then if its type matches the exception named after the
keyword, the except clause is executed, and then execution continues after the
- If an exception occurs which does not match the exception named in the except clause, it is passed on to
statements; if no handler is found, it is an unhandled exception and execution stops with a message as shown above.
The first exceptions is handled first.
>>> try: int('a') try: print(1/0) except: print("I'm handled last") except: print("I'm handled first") I'm handled first
If the inner
try/except clause can’t handle the exceptions they are passed to the outer
>>> try: try: print(1/0) except ValueError: print("I can't handle you") except: print("I handled the error") I handled the error
If no exceptions occurred.
>>> try: i = 1 for j in range(10): print(i) print('yay no exceptions are there') except: print('error') 1 1 1 1 1 1 1 1 1 1 yay no exceptions are there
Raising an Exception in Python
Now that we have gone through the exception catching part, we will cover exception raising part in this section.
An exception is thrown by executing the raise statement, with an appropriate instance of an exception class as an argument that designates the problem. For example, if a function for computing a square root is sent a negative value as a parameter, it can raise an exception with the command:
>>> def divide(i,j): if j == 0: raise ValueError('j cannot be zero') return i/j >>> divide(1,0) Traceback (most recent call last): File "<pyshell#47>", line 1, in <module> divide(1,0) File "<pyshell#46>", line 3, in divide raise ValueError('j cannot be zero') ValueError: j cannot be zero
How extreme we have to go with error checking?
How much error-checking to perform within a function is a matter of debate. Checking the type and value of each parameter demands additional execution time and, if taken to an extreme, seems counter to the nature of Python. Consider the following built-in
sum function which is defined with rigorous error checking.
def sum(values): if not isinstance(values, collections.Iterable): raise TypeError( 'parameter must be an iterable type' ) total = 0 for v in values: if not isinstance(v, (int, float)): raise TypeError( 'elements must be numeric' ) total = total+ v return total
This is an user defined function for
sum which does the same task as in-built
def sum(values): total = 0 for v in values: total = total + v return total
Interestingly, this simple implementation performs exactly like Python’s built-in version of the function. Even without the explicit checks, appropriate exceptions are raised naturally by the code.
Fantastic isn’t it!! Well this is why python is such a beautiful language.
You may check out the full list of Python in-built exception handling classes in this link.
That’s it for this tutorial, In the next tutorial we are going to cover the
User defined Exception Handling part. So don’t forget to subscribe to our newsletter to stay updated.