16. PYTHON EXCEPTIONS AND EXCEPTION HANDLING

Python Errors and Built-in Exceptions:

When writing a program, we will encounter errors.
Error caused by not following the proper structure (syntax) of the language is called syntax error or parsing error.
>>> if a < 3
  File "<interactive input>", line 1
    if a < 3
           ^
SyntaxError: invalid syntax
We can notice here that a colon is missing in the if statement.
Errors can also occur at runtime and these are called Exceptions.
For Example:
When a file we try to open does not exist- FileNotFoundError
Dividing a number by zero ----ZeroDivisionError
Module we try to import is not found ----ImportError etc
Whenever these type of runtime error occur, Python creates an exception object. If not handled properly, it prints a traceback to that error along with some details about why that error occurred.
>>> 1 / 0
Traceback (most recent call last):
 File "<string>", line 301, in runcode
 File "<interactive input>", line 1, in <module>
ZeroDivisionError: division by zero

>>> open("imaginary.txt")
Traceback (most recent call last):
 File "<string>", line 301, in runcode
 File "<interactive input>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'imaginary.txt'

Python Built-in Exceptions

We can view all the built-in exceptions using the local() built-in functions as follows.

>>> locals()['__builtins__']
ExceptionCause of Error
AssertionErrorRaised when assert statement fails.
AttributeErrorRaised when attribute assignment or reference fails.
EOFErrorRaised when the input() functions hits end-of-file condition.
FloatingPointErrorRaised when a floating point operation fails.
GeneratorExitRaise when a generator's close() method is called.
ImportErrorRaised when the imported module is not found.
IndexErrorRaised when index of a sequence is out of range.
KeyErrorRaised when a key is not found in a dictionary.
KeyboardInterruptRaised when the user hits interrupt key (Ctrl+c or delete).
MemoryErrorRaised when an operation runs out of memory.
NameErrorRaised when a variable is not found in local or global scope.
NotImplementedErrorRaised by abstract methods.
OSErrorRaised when system operation causes system related error.
OverflowErrorRaised when result of an arithmetic operation is too large to be represented.
ReferenceErrorRaised when a weak reference proxy is used to access a garbage collected referent.
RuntimeErrorRaised when an error does not fall under any other category.
StopIterationRaised by next() function to indicate that there is no further item to be returned by iterator.
SyntaxErrorRaised by parser when syntax error is encountered.
IndentationErrorRaised when there is incorrect indentation.
TabErrorRaised when indentation consists of inconsistent tabs and spaces.
SystemErrorRaised when interpreter detects internal error.
SystemExitRaised by sys.exit() function.
TypeErrorRaised when a function or operation is applied to an object of incorrect type.
UnboundLocalErrorRaised when a reference is made to a local variable in a function or method, but no value has been bound to that variable.
UnicodeErrorRaised when a Unicode-related encoding or decoding error occurs.
UnicodeEncodeErrorRaised when a Unicode-related error occurs during encoding.
UnicodeDecodeErrorRaised when a Unicode-related error occurs during decoding.
UnicodeTranslateErrorRaised when a Unicode-related error occurs during translating.
ValueErrorRaised when a function gets argument of correct type but improper value.
ZeroDivisionErrorRaised when second operand of division or modulo operation is zero.

We can also define our own exception in Python (if required). Visit this page to learn more about user-defined exceptions.

Python Exception Handling:

Python has many built-in exceptions which forces your program to output an error when something in it goes wrong.
When these exceptions occur, it causes the current process to stop and passes it to the calling process until it is handled. If not handled, our program will crash.
For example:
if function A calls function B which in turn calls function C and an exception occurs in function C. If it is not handled in C, the exception passes to B and then to A.
If never handled, an error message is spit out and our program come to a sudden, unexpected halt.

Catching Exceptions in Python

In Python, exceptions can be handled using a try statement.
A critical operation which can raise exception is placed inside the try clause and the code that handles exception is written in except clause.
It is up to us, what operations we perform once we have caught the exception.
For example:
# import module sys to get the type of exception
import sys
randomList = ['a', 0, 2]
for entry in randomList:
    try:
        print("The entry is", entry)
        r = 1/int(entry)
        break
    except:
        print("Oops!",sys.exc_info()[0],"occured.")
        print("Next entry.")
        print()
print("The reciprocal of",entry,"is",r)
OUTPUT:

The entry is a
Oops! <class 'ValueError'> occured.
Next entry.

The entry is 0
Oops! <class 'ZeroDivisionError' > occured.
Next entry.

The entry is 2
The reciprocal of 2 is 0.5

Catching Specific Exceptions in Python

In the above example, we did not mention any exception in the except clause.
This is not a good programming practice as it will catch all exceptions and handle every case in the same way. We can specify which exceptions an except clause will catch.
A try clause can have any number of except clause to handle them differently but only one will be executed in case an exception occurs.
We can use a tuple of values to specify multiple exceptions in an except clause. 
try:
   # do something
   pass

except ValueError:
   # handle ValueError exception
   pass

except (TypeError, ZeroDivisionError):
   # handle multiple exceptions
   # TypeError and ZeroDivisionError
   pass

except:
   # handle all other exceptions
   pass

Raising Exceptions

In Python programming, exceptions are raised when corresponding errors occur at run time, but we can forcefully raise it using the keyword raise.
We can also optionally pass in value to the exception to clarify why that exception was raised.
>>> raise KeyboardInterrupt
Traceback (most recent call last):
...
KeyboardInterrupt

>>> raise MemoryError("This is an argument")
Traceback (most recent call last):
...
MemoryError: This is an argument

>>> try:
...     a = int(input("Enter a positive integer: "))
...     if a <= 0:
...         raise ValueError("That is not a positive number!")
... except ValueError as ve:
...     print(ve)
...    
Enter a positive integer: -2
That is not a positive number!

try...finally

The try statement in Python can have an optional finally clause. This clause is executed no matter what, and is generally used to release external resources.
For example:
we may be connected to a remote data center through the network or working with a file or working with a Graphical User Interface (GUI).
try:
   f = open("test.txt",encoding = 'utf-8')
   # perform file operations
finally:
   f.close()
This type of construct makes sure the file is closed even if an exception occurs.

No comments:

Post a Comment