A Python program can break due to various types of Errors. We shall explore few of these errors in the post. To make best use of this post, you should know certain topics in Python3 such as datatypes, expression, pass statement, etc.

Not all errors are significant, and hence there would be cases where we can skip certain errors to uninterrupt execution of our code, and this is achieved with try except blocks.

ZeroDivisionError

This error is returned when we try to divide any number by 0

>>> 50 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero

This time we tell Python not to break execution, by including things in a try except block.

>>> try:
...     0 /0
... except ZeroDivisionError:
...     pass
...

The expression 00/0 in the try block should have produced a ZeroDivisionError, but since we are saying except ZeroDivisionError, that error would be excepted. So there wont be any error and there should’nt be any output as well as we have included pass in the except block. Note that pass is used as a placeholder where you don’t want the program to any program, and just to go the next line of the code in sequence if any.

Let’s try a different example, and also try printing some info instead of pass.

>>> try:
...     120 / 0
... except:
...     print('oh there was an exception')
...
oh there was an exception

TypeError

We can try to do an unsupported math operation, for example division, between a string and an integer, to trigger this error

>>> 30 / 'hello'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for /: 'int' and 'str'

Another example, but we include an exception for the error this time

>>> try:
...    'text' + 20
... except TypeError:
...     print('There was a type error')
...
There was a type error

NameError

When we try calling a variable a.k.a object name, that was not defined before, we should get a NameError

>>> print(name)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'name' is not defined

Error on improper exception

Error would still occur if the except statement doesn’t include the correct error type.

>>> try:
...    'text' + 20
... except ValueError:  # wont process as we get TypeError above
...     print('There was a value error')
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
TypeError: can only concatenate str (not "int") to str

We get an error cause, as we tried to except ValueError, where as the statement in try block was producing TypeError

Exception for Multiple Errors

We can include exceptions for multiple error types by separating each error type by a comma. The following example should have produced NameError.

>>> try:
...     print(name)
... except(NameError, TypeError):
...     print('There was a NameError or TypeError')
...
There was a NameError or TypeError
>>>

Another example for multiple exceptions, however it should have generated a TypeError

>>> name = 'Michael'
>>>
>>> try:
...     print(name)
...     'text' - 20
... except(NameError, TypeError):
...     print('There was a NameError or TypeError')
...
Michael
There was a NameError or TypeError

Or we could even provide exceptions for any error by jus saying expect with out any error type. Let’s try printing the variable city, which is not defined before, this should have caused a NameError

>>> try:
...     print(city)
... except:
...     print('oh! there was an exception error')
...
oh! there was an exception error

Note that in the snippet above, we have excepted all types of Errors and not just NameError.

One more example for providing exception to all errors

>>> try:
...     print(y)
... except:
...     print('There was an error, But that\'s ok, go ahead with the rest of the code')
...
There was an error, But that's ok, go ahead with the rest of the code
>>> print('Thank you')
Thank you
>>>

Its sometimes much apt to print the message the error would have been generated for, instead of printing a user defined message

>>> try:
...    print(name)
... except NameError as n:
...     print(n)
...
name 'name' is not defined

So the mesage name 'name' is not defined is coming straight from the NameError itself, and says why there was a NameError. Here we have caught the error message as variable n by saying NameError as n, and then we print(n) it displays the error message or reason stored.

One more example

>>> try:
...     'string ' + 2
... except TypeError as t:
...     print(t)
...
can only concatenate str (not "int") to str

This time it printed the reason for the TypeError

Raise Errors

We could even raise errors manually with the raise statement.

>>> try:
...     raise TypeError()
... except TypeError as t:
...     print(t)
...

It shouldn’t print anything cause we raised the error manually, and there wasn’t any standard error message, providing the reason on why it was generated.

We could however print a user defined message.

>>> try:
...     raise TypeError()
... except TypeError:
...     print('The type error was raised manually')
...
The type error was raised manually

Class with Multi level inheritance

The errors we have seen so far belong to a predefined class called Exception.

>>> print(ValueError.__bases__)
(<class 'Exception'>,)

>>> print(TypeError.__bases__)
(<class 'Exception'>,)

Note that bases is used to find the base class of a class.

Let’s define a class called Base that would inherit the Exception class. We are not defininig any unique attributes or methods for this class, and hence say pass.

>>> class Base(Exception):
...     pass
...

Now another class called Child, which would inherit the class ‘Base’.

>>> class Child(Base):
...     pass
...

And the Grandchild class to further inherit from Child.

>>> class Grandchild(Child):
...     pass
...

We are going to loop over these classes, to raise that exception in each iteration, so that the except block would be executed.

>>> for i in [ Base, Child, Grandchild ]:
...     try:
...         raise i()
...     except Grandchild:
...         print("Grand child exception")
...     except Child:
...         print("Child exception")
...     except Base:
...         print("Base exception")
...
Base exception
Child exception
Grand child exception

The loop above was straight forward, i.e. when the Base exception was raised, it would print ‘Base exception’.

Let’s now reverse the order of except statements and try.

>>> for i in [ Base, Child, Grandchild ]:
...     try:
...         raise i()
...     except Base:
...         print("Base exception")
...     except Child:
...         print("Child exception")
...     except Grandchild:
...         print("Grand child exception")
...
Base exception
Base exception
Base exception
>>>

Each time the Base exception will be caught, as both Child and Grandchild inherit Base. And the except block for Base was written first.

Custom message

Define a custom exception as a sub / derived class of the Exception class. Define a new class MyException as a derived class of the Exception class.

>>> class MyException(Exception):
...     pass
...

Define InputException as a derived class of MyException. So InputException also inherits Exception. However a custom message is defined for InputException, so it won’t inherit this message from Exception.

>>> class InputException(MyException):
...     def __init__(self, message):
...          self.message = message
...

Create an instance ‘i’ of the InputException class, and pass argument for message.

>>> i = InputException('This is a user defined exception')
>>>

We can now raise i, i.e. an InputException instance, to create an error manually, and the screen would throw the custom message.

>>> raise i
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
__main__.InputException: This is a user defined exception

__main__ is the value of the __name__ keyword, its used to fetch the name of an object, and by default it’s __main__. It means the value of __name__ in our program or module is always __main__, and it has global scope, its like the name of the overall current module.

>>> print(__name__)
__main__

Each class will also have this attribute. For instance the __name__ atrribute of InputException is its name itself, same is the case for Exception.

>>> print(InputException.__name__)
InputException

>>> print(Exception.__name__)
Exception

SyntaxError

We have seen errors than can be excepted, but there are also Errors that can not be excepted. For instance IndentationError can not be excepted.

>>> if True:
...     print('hi')
...   print('hello')
  File "<stdin>", line 3
    print('hello')
                 ^
IndentationError: unindent does not match any outer indentation level

Indentation error is derived class of SyntaxError.

>>> print(IndentationError.__bases__)
(<class 'SyntaxError'>,)

Make a syntax error directly.

>>> print('Hello)
  File "<stdin>", line 1
    print('Hello)
                ^
SyntaxError: EOL while scanning string literal

SyntaxError inherits Exception, which in turn inherits BaseException.

>>> print(SyntaxError.__bases__)
(<class 'Exception'>,)
>>> print(Exception.__bases__)
(<class 'BaseException'>,)
>>> print(BaseException.__bases__)
(<class 'object'>,)
>>> print(object.__bases__)
()
>>>

–end-of-post–