Exceptions special objects used by Python to manage errors
that arise during a program’s execution. An exception object is created by
Python whenever any error occurs that makes Python unsure what to do. If the
exceptions are handled by our programs it continues to run otherwise it halts
and shows a traceback including a report of the raised exception.
The simple programs usually behave exactly as expected but as
programs get bigger and more complex, their behavior becomes harder to predict.
For example, a program might have a configuration file: what happens if that
file contains settings that are unsuitable or if the file doesn’t exist at all?
Almost certainly, programs will use input data that will change over time: what
if the data is corrupted or somehow unusable? And what if another system
resource, for example, a network connection needed to contact a remote web service
to obtain additional data, is unavailable? Worse, what if any number of these
abnormal events occur in some method that you use many times throughout your
code? Sometimes, you simply don’t care about the error but other times you do:
how can your program handle errors intelligently in this scenario?
The answer is through exception handling.
Exception Handling in Python
In Python Exceptions are handled with try-except blocks. A
try-except block asks Python to do something, but it also tells Python what to
do if an exception is raised. When we use try-except blocks, our programs will
continue running even if things start to go wrong. Instead of tracebacks, which
can be confusing for users to read, users will see friendly error messages that
we write.
Every exception has a certain type. The types in the
example are ZeroDivisionError, NameError, and TypeError.
Handling Simple
Exceptions
Let’s begin with one of the most basic errors that a program
can encounter: trying to divide a number by zero. You’ll see how Python’s own
exception handling deals with the problem and how it can be used in quite
subtle ways to turn a simple error into intelligent reporting and management.
Consider the following example-
class Calculation():
def
multiply(self,x,y):
result
= x*y
return result
def
division(self,a,b):
result
= a/b
return
result
num1 = int(input('Enter first digit: '))
num2 = int(input('\nEnter second digit: '))
obj1= Calculation()
mul = obj1.multiply(num1,num2)
print("\nThe multiplied value is: " + str(mul))
div = obj1.division(num1,num2)
print("\nThe divided value is: " + str(div))
When we run this program as shown below we see that an error
is reported in the traceback , the
ZeroDivisionError, which is an exception object.
In fact, the report you’re reading is the final stage in a
procedure that Python has already gone through to try to handle the underlying
problem. Let’s look at that procedure in more detail. As in written arithmetic,
the division operator takes two arguments, a dividend to the left and a divisor
to the right. The operation is quite simple, but the operator has a number of
checks built into it; for example, both of its arguments must be numeric, and
the divisor must be nonzero. Usually, these conditions are expected to be satisfied.
However, the operator itself has no way of recovering when one of these checks
fails. The operator instead falls back on Python’s standard method of dealing
with exceptional circumstances, exceptions.
If the divisor is zero, the operator causes an exception
object of class ZeroDivisionError to be created. We then say that this
exception is raised by the operator (or by the attempted operation). This can
be taken to mean “raising” in the sense of discussing a problem at work with
your boss and, more widely, in the sense of a problem passing up through a
chain of command. When function A calls function B, and function B includes a
division by zero, the division operator raises the exception within function B.
And if function B doesn’t know how to handle the exception, it raises the
exception with function A.
Ultimately in our example, the exception is raised with the
interactive mode, which doesn’t know how to handle the circumstances and must
therefore raise the exception with you, the programmer. This needs to be done
in a human-readable way, which results in the error report. This is usually
called a traceback, which we’ll discuss in more detail later.
Some programming languages talk of throwing and catching
exceptions rather than raising them: the division operator throws an exception
up to function B; if function B doesn’t catch that exception, it carries on to
function A. This is just another metaphor for the same underlying process.
Python creates an exception object in response to a
situation where it can’t do what we ask it to. When this happens, Python stops
the program and tells us the kind of exception that was raised. We can use this
information to modify our program. We’ll tell Python what to do when this kind
of exception occurs; that way, if it happens again, we’re prepared. We use the
try-except block to tell Python what to do when this kind of exception occurs.
Using the try
Statement with an except Clause in Python
Let's discuss try...except in detail. First see the
syntax:
try:
operation block;
except Exception_name:
If there is
Exception_name1 then execute this block.
except Exception_name2:
If there is
Exception_name2, then execute this block.
else:
If there is no exception then
execute this block.
If the exception is created deep in a program (e.g.,
within a method), it can be caught and dealt with in between the original error and the program’s output.
Python’s try statement is used to deal with
exceptions, and in its most basic form, it can be used as
shown in the code below where we use a try-except block for handling the
ZeroDivisionError exception.
def division(self,a,b):
try:
result
= a/b
return
result
except:
print("\nOops!
You can't divide by zero!")
We put the lines that caused the error, inside a try
block. If the code in a try block works, Python skips over the except block. If
the code in the try block causes an error, Python looks for an except block
whose error matches the one that was raised and runs the code in that block.
In this example, the code in the try block produces a
ZeroDivisionError, so Python looks for an except block telling it how to
respond. Python then runs the code in that block, and the user sees a friendly
error message instead of a traceback.
After making the necessary changes in the program when we
run it we get the output as shown below:
The try-except-else block works like this: Python
attempts to run the code in the try statement. The only code that should go in
a try statement is code that might cause an exception to be raised. Sometimes
you’ll have additional code that should run only if the try block was
successful; this code goes in the else block. The except block tells Python
what to do in case a certain exception arises when it tries to run the code in
the try statement.
Thus we can modify our program to include an else block
as shown below-
def division(self,a,b):
try:
result
= a/b
return
result
except:
print("\nOops!
You can't divide by zero!")
else:
print(result)
By anticipating likely sources of errors, you can write
robust programs that continue to run even when they encounter invalid data and
missing resources. Your code will be resistant to innocent user mistakes and
malicious attacks.
In the given program, when we give the input from the
keyboard, the input string will be
converted into int type. When we supply a string instead
of a number, then the interpreter returns a message with a ValueError error as
shown below:
When number 0 is supplied, then ZeroDivisionError is
returned. By using multiple exception blocks, we can handle both the
exceptions. See the program below:
class Calculation():
def
multiply(self,x,y):
result
= x*y
return
result
def
division(self,a,b):
try:
result
= a/b
return
result
except
ZeroDivisionError:
print("\nOops!
You can't divide by zero!")
else:
print(result)
try:
num1
= int(input('Enter first digit: '))
num2
= int(input('\nEnter second digit: '))
obj1=
Calculation()
mul
= obj1.multiply(num1,num2)
print("\nThe
multiplied value is: " + str(mul))
div
= obj1.division(num1,num2)
print("\nThe
divided value is: " + str(div))
except ValueError:
print("\nYou
did't input int value!")
Now if we run the program with a non-integer input the
ValueError exception will be raised which is handled by the program. See the
output below:
In the preceding output, customized message, “You did't
input int value!" has been displayed so that the user can understand his
mistake.
Now suppose by mistake we changed a variable name or used
an undefined variable in our program as
shown below:
num1 = int(input('Enter first digit: '))
num2 = int(input('\nEnter second digit: '))
obj1=
Calculation()
mul
= obj1.multiply(num1,num)
print("\nThe
multiplied value is: " + str(mul))
Instead of passing num2, I passed num as a parameter in
the multiply(). See the output below:
We can handle this NameError exception also in our
program as shown below:
try:
num1
= int(input('Enter first digit: '))
num2
= int(input('\nEnter second digit: '))
obj1=
Calculation()
mul
= obj1.multiply(num1,num)
print("\nThe
multiplied value is: " + str(mul))
div
= obj1.division(num1,num2)
print("\nThe
divided value is: " + str(div))
except ValueError:
print("\nYou
did't input int value!")
except NameError:
print("\nCheck
the variable names you used!")
The output is shown below:
Another common issue when working with files is handling
missing files. The file you’re looking for might be in a different location,
the filename may be misspelled, or the file may not exist at all. You can
handle all of these situations in a straightforward way with a try-except
block.
Let’s try to read a file that doesn’t exist. The
following program tries to read the cities from the file cities.txt but I
haven’t saved the file cities.txt in the same directory as city.py:
filename = 'cities.txt'
with open(filename) as file_obj:
contents
= file_obj.read()
print(contents)
Python can’t read from a missing file, so it raises an
exception as shown below:
The last line of the traceback reports a
FileNotFoundError: this is the exception Python creates when it can’t find the
file it’s trying to open. In this example, the open() function produces the
error, so to handle it, the try block will begin just before the line that
contains open():
filename = 'cities.txt'
try:
with
open(filename) as file_obj:
contents
= file_obj.read()
except FileNotFoundError:
print("\nThe
file "+ filename + " doesn't exists!")
else:
print(contents)
In this example, the code in the try block produces a
FileNotFoundError, so Python looks for an except block that matches that error.
Python then runs the code in that block, and the result is a friendly error
message instead of a traceback:
If I move the file cities.txt to the same directory as
that of cities.py, the content of this file will be printed as shown below:
The try...finally
statement
In a situation where you are completely sure that a
certain block of code will be executed whether the program throws exceptions or
not, try...finally is useful. Consider the situation when you open a file and
read the input, but for some reason the program throws an exception and the
file you want is closed whether the exception occurs or not, then try...finally
will solve the problem.
The syntax is as follows:
try:
#run this action first
except:
# Run if exception occurs
Finally :
#Always run this code
The order of the statement should be: try -> except
-> else -> finally. See the following program-
filename = 'cities1.txt'
try:
with
open(filename) as file_obj:
contents
= file_obj.read()
except FileNotFoundError:
print("\nThe
file "+ filename + " doesn't exists!")
else:
print(contents)
finally:
print("\nProgram
ends!")
The following program tries to read the cities from the
file cities1.txt but I haven’t saved the file cities1.txt in the same directory
as city.py. Python can’t read from a missing file, so it raises an exception as
shown below:
In the preceding output the try, except and finally
blocks have been executed.
The exception argument
When you write a program, it is very mundane and tedious
to write each and every exception type. Instead of writing each exception, you
could use just one line. See the program below:
filename = 'cities1.txt'
try:
with
open(filename) as file_obj:
contents
= file_obj.read()
except Exception as e:
print(e,type(e))
else:
print(contents)
finally:
print("\nProgram
ends!")
The preceding code catches the exception as e and type(e)
displays its exception type. Let's see the output. In the code, e is the
argument of the exception: the contents referred by the argument vary by
exception:
User-defined exceptions
Python allows you to define your own exceptions.
Exceptions should be inherited from the Exception class. See the example below:
class MyException(Exception):
def
__init__(self, value):
self.value
= value
def
__str__(self):
return
(self.value)
try:
num
= input("Enter the number : ")
if
num == '2':
raise
MyException("ohh")
else
:
print
("\nnumber is not 2")
except MyException :
print
("\nMy exception occurred")
The preceding code defined the MyException class, which
inherits the base class Exception. In this example, the default __init__()
exception has been overridden. The code
in the try block raises the user-defined exception if you pass the value 2. The
raised exception is handled by the except block. The raise statement allows the
programmer to trigger specific exceptions explicitly. See the output of the
program:
In the preceding code, if you pass the value 2, then it
gives a user-defined custom error. If you pass a number other than 2, then no
error occurs.
Without handling exceptions you cannot write standard
code so it’s better to develop a habit of incorporating exception handling
while coding. This is the end of today’s topic. Till we meet next keep
practicing and learning Python as Python is easy to learn!
0 comments:
Post a Comment