While executing a python program we may encounter errors. There are 2 types of errors:
If Exceptions are not handled properly, the program will crash and come to a sudden & unexpected halt.
In [1]:
print('Hello)
In [2]:
1 / 0
In [3]:
open('doesnotexistfile.txt')
In [7]:
print(locals()['__builtins__'])
Following are some of the built-in exceptions.
Python provides 'try/except' statements to handle the exceptions. The operation which can raise exception is placed inside the 'try' statement and code that handles exception is written in the 'except' clause.
In [11]:
import sys
def divide(a,b):
try:
return a / b
except:
print(sys.exc_info()[0])
divide (1,2)
divide (2,0) # This will be captured by the 'except' clause
In [12]:
# print custom error message
def divide(a,b):
try:
return a / b
except:
print('Error occured',sys.exc_info()[0])
divide (1,2)
divide (2,0) # This will be captured by the 'except' clause
In [18]:
def divide(a,b):
try:
return a / b
except (ZeroDivisionError):
print('Number cannot be divided by zero or non-integer')
except:
print('Error Occured',sys.exc_info()[0])
divide (1,2)
divide (2,0) # This will be captured by the 'except - zero division error' clause
divide (2,'a') # This will be captured by the generic 'except' clause
In [19]:
def divide(a,b):
try:
return a / b
except (ZeroDivisionError, TypeError): # use a tuple to capture multiple errors
print('Number cannot be divided by zero or non-integer')
except:
print('Error Occured',sys.exc_info()[0])
divide (1,2)
divide (2,0) # This will be captured by the 'except - zero division error' clause
divide (2,'a') # This will be captured by the generic 'except' clause
The last except clause may omit the exception name(s), to serve as a wildcard. Use this with extreme caution, since it is easy to mask a real programming error in this way! It can also be used to print an error message and then re-raise the exception (allowing a caller to handle the exception as well):
In [20]:
import sys
try:
f = open('myfile.txt')
s = f.readline()
i = int(s.strip())
except OSError as err:
print("OS error: {0}".format(err))
except ValueError:
print("Could not convert data to an integer.")
except:
print("Unexpected error:", sys.exc_info()[0])
raise
The try … except statement has an optional else clause, which, when present, must follow all except clauses. It is useful for code that must be executed if the try clause does not raise an exception. For example:
In [21]:
for arg in sys.argv[1:]:
try:
f = open(arg, 'r')
except OSError:
print('cannot open', arg)
else:
print(arg, 'has', len(f.readlines()), 'lines')
f.close()
The use of the else clause is better than adding additional code to the try clause because it avoids accidentally catching an exception that wasn’t raised by the code being protected by the try … except statement.
The except clause may specify a variable after the exception name. The variable is bound to an exception instance with the arguments stored in instance.args. For convenience, the exception instance defines str() so the arguments can be printed directly without having to reference .args.
In [40]:
try:
raise Exception('1002','Custom Exception Occured')
except Exception as inst:
print(type(inst))
print(inst)
print(inst.args)
errno, errdesc = inst.args
print('Error Number:',errno)
print('Error Description:',errdesc)
Exception handlers don’t just handle exceptions if they occur immediately in the try clause, but also if they occur inside functions that are called (even indirectly) in the try clause. For example:
In [38]:
def func_will_fail():
return 1 / 0
try:
func_will_fail()
except ZeroDivisionError as err:
print('Handling Error - ',err)
In [35]:
raise NameError('Error Occured')
If you need to determine whether an exception was raised but don’t intend to handle it, a simpler form of the raise statement allows you to re-raise the exception:
In [36]:
try:
raise NameError('Error Captured')
except NameError:
print('Captured Exception')
raise
Python has many built-in exceptions which forces your program to output an error when something in it goes wrong.
However, sometimes you may need to create custom exceptions that serves your purpose.
In Python, users can define such exceptions by creating a new class. This exception class has to be derived, either directly or indirectly, from Exception class. Most of the built-in exceptions are also derived form this class.
In [30]:
class CustomError(Exception):
pass
In [31]:
raise CustomError()
In [32]:
raise CustomError('Unexpected Error Occured')
Here, we have created a user-defined exception called CustomError which is derived from the Exception class. This new exception can be raised, like other exceptions, using the raise statement with an optional error message.
When we are developing a large Python program, it is a good practice to place all the user-defined exceptions that our program raises in a separate file. Many standard modules do this. They define their exceptions separately as exceptions.py or errors.py (generally but not always).
Most exceptions are defined with names that end in “Error,” similar to the naming of the standard exceptions.
In [33]:
# define Python user-defined exceptions
class Error(Exception):
"""Base class for other exceptions"""
pass
class ValueTooSmallError(Error):
"""Raised when the input value is too small"""
pass
class ValueTooLargeError(Error):
"""Raised when the input value is too large"""
pass
# our main program
# user guesses a number until he/she gets it right
# you need to guess this number
number = 10
while True:
try:
i_num = int(input("Enter a number: "))
if i_num < number:
raise ValueTooSmallError
elif i_num > number:
raise ValueTooLargeError
break
except ValueTooSmallError:
print("This value is too small, try again!")
print()
except ValueTooLargeError:
print("This value is too large, try again!")
print()
print("Congratulations! You guessed it correctly.")
Here, we have defined a base class called Error.
The other two exceptions (ValueTooSmallError and ValueTooLargeError) that are actually raised by our program are derived from this class. This is the standard way to define user-defined exceptions in Python programming.
Many standard modules define their own exceptions to report errors that may occur in functions they define. A detailed example is given below:
In [34]:
class Error(Exception):
"""Base class for exceptions in this module."""
pass
class InputError(Error):
"""Exception raised for errors in the input.
Attributes:
expression -- input expression in which the error occurred
message -- explanation of the error
"""
def __init__(self, expression, message):
self.expression = expression
self.message = message
class TransitionError(Error):
"""Raised when an operation attempts a state transition that's not
allowed.
Attributes:
previous -- state at beginning of transition
next -- attempted new state
message -- explanation of why the specific transition is not allowed
"""
def __init__(self, previous, next, message):
self.previous = previous
self.next = next
self.message = message
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.
A finally clause is always executed before leaving the try statement, whether an exception has occurred or not. When an exception has occurred in the try clause and has not been handled by an except clause (or it has occurred in an except or else clause), it is re-raised after the finally clause has been executed.
The finally clause is also executed “on the way out” when any other clause of the try statement is left via a break, continue or return statement.
In [24]:
try:
raise KeyBoardInterrupt
finally:
print('Bye')
In [26]:
def divide(a,b):
try:
result = a / b
except ZeroDivisionError:
print('Number cannot be divided by zero')
else:
print('Result',result)
finally:
print('Executed Finally Clause')
In [27]:
divide(2,1)
In [28]:
divide(2,0)
In [29]:
divide('1','2')
Please note that the TypeError raised by dividing two strings is not handled by the except clause and therefore re-raised after the finally clause has been executed.
In [ ]:
for line in open("myfile.txt"):
print(line, end="")
The problem with this code is that it leaves the file open for an indeterminate amount of time after this part of the code has finished executing. This is not a best practice.
In [23]:
with open("test.txt") as f:
for line in f:
print(line, end="")
The with statement allows objects like files to be used in a way that ensures they are always cleaned up promptly and correctly.
After the statement is executed, the file f is always closed, even if a problem was encountered while processing the lines.