Sunday, August 23, 2015

Python Basics 015: Exception Handling

Previous chapter: Classes and Objects Next Chapter: Dictionaries



Python Basics - 014: Exception Handling

This is somewhat an advanced chapter, it is very much relevant for beginners of python but have working knowledge of exceptions in other languages like Java.

Beginners to programming can revisit this chapter at your leisure!

Exceptions

Exceptions[1] are errors which happen in code even though everything is syntactically correct, errors which happen during runtime are called exceptions.

Most of the exceptions are due to environmental reasons, for example trying to access a file which is not available, trying to make a request to a server which is not reachable or simply the user has input some unexpected values to your program.

Let's see some examples:

Anything divided by 0 will result in infinity which is not understood by computers and is called a divide by zero exception:


a = 1/0

will result in


Traceback (most recent call last):
  File "/PycharmProjects/Tutorial/ExceptionHandling.py", line 1, in <module>
    a = 1/0
ZeroDivisionError: division by zero

Using a variable which is not defined. This may seem like a syntax or coding error, but remember from last chapter that we could add and removed variables at run time.


b = x * 10

will result in


Traceback (most recent call last):
  File "/PycharmProjects/Tutorial/ExceptionHandling.py", line 6, in <module>
    b = x * 10
NameError: name 'x' is not defined

Using incompatible types in a concat operation:


c = "String" + 42

will result in


Traceback (most recent call last):
  File "/PycharmProjects/Tutorial/ExceptionHandling.py", line 11, in <module>
    c = 'String' + 42
TypeError: Can't convert 'int' object to str implicitly

We can see that the exception is very informative about the problem the computer is facing, python tends to give us as much information as possible. We can see that python

  • shows the file name,
  • the line in which the error is happening,
  • the module/function name,
  • it quotes the line which causes the exception,
  • mentions the name of the built-in exception[2]
  • and the error message.

Exception Handling

Exception handling[3] is the way we handle errors occurring in the code at run time, we make provisions in the code to anticipate some errors and handle them properly instead of them crashing/exiting the application.

Like most other languages, Python also uses the try block concept: try-except

We'll simulate a scenario which causes a divide by zero error, in the following example assume the arguments of the function to be from user input. The program aims to print product and quotient of simple division when user supplies the values.


def printProductandQuotient(var1,var2):
    product = var1 * var2
    quotient = var1 / var2
    print("product of var1 and var2 is:", product)
    print("quotient when dividing",var1, "with", var2, "is:", quotient)

printProductandQuotient(10, 2)
printProductandQuotient(10, 0)

This is the implementation that we would normally do, but if you look at the second function call, we are passing 10, 0 because the user input them. We know that dividing 10 by 0 will result in an exception and the program will effectively exit.

The output will be:


product of var1 and var2 is: 20
quotient when dividing 10 with 2 is: 5.0
Traceback (most recent call last):
  File "/PycharmProjects/Tutorial/ExceptionHandling.py", line 15, in <module>
    printProductandQuotient(10, 0)
  File "/PycharmProjects/Tutorial/ExceptionHandling.py", line 10, in printProductandQuotient
    quotient = var1 / var2
ZeroDivisionError: division by zero

Now let me try and rewrite the same program to handle divide by zero exceptions and print another message instead of crashing.


def printProductandQuotientProper(var1,var2):
    product = var1 * var2
    print("product of var1 and var2 is:", product)
    try:
        quotient = var1 / var2
        print("quotient when dividing", var1, "with", var2, "is:", quotient)
    except ZeroDivisionError:
        print("we cannot divide a number by 0, it results in infinity")

printProductandQuotientProper(10, 2)
printProductandQuotientProper(10, 0)

Now we are doing the division inside a try block, any ZeroDivisionError that occurs will be caught by the except and we'll print the custom message. Now the output is available and we haven't crashed the application!


product of var1 and var2 is: 20
quotient when dividing 10 with 2 is: 5.0
product of var1 and var2 is: 0
we cannot divide a number by 0, it results in infinity

We can use multiple except: blocks for a single try: incase there is a possibility of other exceptions occurring.

You can also define custom exception classes[4] for complicated cases, however it is not the time to go into that, I've never used custom exceptions in java in all my years of coding, it is just overkill.

Finally()

In try-except block there is another element that does cleanup work[5]. It is another block which is executed irrespective of exceptions. If code enters into the try block, the finally block will definitely be executed.

It lets us do some last minute cleanups in case there is an application crash or smoother exception which we have not mentioned in the except: block.


def testFinally():
    try:
        x = 1/0
    except TypeError:
        print("there was a type error in the code")
    finally:
        print("some other exception occurred, gracefully shutting down")
        ''' do something here to gracefully shutdown app'''

testFinally()

Here's a sample code, in it we are not catching the proper exception, so the application will still crash even if we have the try block, but while exiting the finally block is also executed.

So the output will be:


Traceback (most recent call last):
  File "/PycharmProjects/Tutorial/ExceptionHandling.py", line 39, in <module>
    testFinally()
  File "/PycharmProjects/Tutorial/ExceptionHandling.py", line 32, in testFinally
    x = 1/0
ZeroDivisionError: division by zero
some other exception occurred, gracefully shutting down

Exceptions as we've seen are essential for a crash-free program. Let's move on the next chapter: Dictionaries


P.S: all the code used in this tutorial is available in github


References

  1. https://docs.python.org/3.4/tutorial/errors.html#exceptions
  2. https://docs.python.org/3.4/library/exceptions.html#bltin-exceptions
  3. https://docs.python.org/3.4/tutorial/errors.html#handling-exceptions
  4. https://docs.python.org/3.4/tutorial/errors.html#user-defined-exceptions
  5. https://docs.python.org/3.4/tutorial/errors.html#defining-clean-up-actions



Previous chapter: Classes and Objects Next Chapter: Dictionaries

No comments:

Post a Comment