1. In Python, there is a distinction between two kinds of errors:
- Syntax errors (parsing errors), which occur when the parser comes across a statement that is incorrect. For example:
Trying to execute the following line:1print("Hello, World!)will cause a SyntaxError, and result in the following (or similar) message being displayed in the console:
123File "main.py", line 1print("Hello, World!)SyntaxError: EOL while scanning string literal
- Exceptions, which occur even when a statement/expression is syntactically correct; these are the errors that are detected during execution when your code results in an error which is not uncoditionally fatal. For example:
Trying to execute the following line:1print(1/0)
will cause a ZeroDivisionError exception, and result in the following (or similar) message being displayed in the console:1234Traceback (most recent call last):File "main.py", line 1, inprint(1/0)ZeroDivisionError: division by zeroThe last line of the error message actually tells you what happened. There are many different types of exceptions, such as ZeroDivisionError, NameError, TypeError, and many more; and this part of the message informs you of what type of exception has been raised. The preceding lines show you the context in which the exception has occured.
Python 3 defines 63 built-in exceptions, and all of them form a tree-shaped hierarchy, although the tree is a bit weird as its root is located on top.
Some of the built-in exceptions are more general (they include other exceptions) while others are completely concrete (they represent themselves only). We can say that the closer to the root an exception is located, the more general (abstract) it is. In turn, the exceptions located at the branches’ ends (we can call them leaves) are concrete.

Note:
ZeroDivisionError is a special case of more a general exception class named ArithmeticError;
ArithmeticError is a special case of a more general exception class named just Exception;
Exception is a special case of a more general class named BaseException;
We can describe it in the following way (note the direction of the arrows – they always point to the more general entity):
BaseException
↑
Exception
↑
ArithmeticError
↑
ZeroDivisionError
2. You can “catch” and handle exceptions in Python by using the try-except block. So, if you have a suspicion that any particular snippet may raise an exception, you can write the code that will gracefully handle it, and will not interrupt the program. Look at the example:
|
1 2 3 4 5 6 7 |
while True: try: number = int(input("Enter an integer number: ")) print(number/2) break except: print("Warning: the value entered is not a valid number. Try again...") |
The code above asks the user for input until they enter a valid integer number. If the user enters a value that cannot be converted to an int, the program will print
Warning: the value entered is not a valid number. Try again..., and ask the user to enter a number again. What happens in such a case?
- The program enters the while loop.
- The
tryblock/clause is executed. The user enters a wrong value, for example:hello!. - An exception occurs, and the rest of the
tryclause is skipped. The program jumps to theexceptblock, executes it, and then continues running after the try-except block.
If the user enters a correct value and no exception occurs, the subsequent instructions in the try block are executed.
|
1 2 3 4 5 6 7 8 9 10 11 |
while True: try: number = int(input("Enter an int number: ")) print(5/number) break except ValueError: print("Wrong value.") except ZeroDivisionError: print("Sorry. I cannot divide by zero.") except: print("I don't know what to do...") |
You can use multiple except blocks within one try statement, and specify particular exception names. If one of the
except branches is executed, the other branches will be skipped. Remember: you can specify a particular built-in exception only once. Also, don’t forget that the default (or generic) exception, that is the one with no name specified, should be placed at the bottom of the branch (use the more specific exceptions first, and the more general last).
You can also specify and handle multiple built-in exceptions within a single except clause:
|
1 2 3 4 5 6 7 8 9 |
while True: try: number = int(input("Enter an int number: ")) print(5/number) break except (ValueError, ZeroDivisionError): print("Wrong value or No division by zero rule broken.") except: print("Sorry, something went wrong...") |
4. Some of the most useful Python built-in exceptions are: ZeroDivisionError, ValueError, TypeError, AttributeError, and SyntaxError. One more exception that, in our opinion, deserves your attention is the KeyboardInterrupt exception, which is raised when the user hits the interrupt key (CTRL-C or Delete).
To learn more about the Python built-in exceptions, consult the official Python documentation.
5. Last but not least, you should remember about testing and debugging your code. Use such debugging techniques as print debugging; if possible – ask someone to read your code and help you to find bugs in it or to improve it; try to isolate the fragment of code that is problematic and susceptible to errors: test your functions by applying predictable argument values, and try to handle the situations when someone enters wrong values; comment out the parts of the code that obscure the issue. Finally, take breaks and come back to your code after some time with a fresh pair of eyes.
6. You cannot add more than one anonymous (unnamed) except branch after the named ones.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
: # The code that always runs smoothly. : try: : # Risky code. : except Except_1: # Crisis management takes place here. except Except_2: # We save the world here. except: # All other issues fall here. : # Back to normal. : |
7. All the predefined Python exceptions form a hierarchy, i.e. some of them are more general (the one named BaseException is the most general one) while others are more or less concrete (e.g. IndexError is more concrete than LookupError).
You should avoid placing more general exceptions before more specific ones inside the same except branche sequence. For example, you can do this:
|
1 2 3 4 5 6 |
try: # Risky code. except IndexError: # Taking care of mistreated lists except LookupError: # Dealing with other erroneous lookups |
but don’t do that (unless you’re absolutely sure that you want some part of your code to be useless)
|
1 2 3 4 5 6 |
try: # Risky code. except LookupError: # Dealing with erroneous lookups except IndexError: # You'll never get here |
Remember:
- the order of the branches matters!
- don’t put more general exceptions before more concrete ones;
- this will make the latter one unreachable and useless;
- moreover, it will make your code messy and inconsistent;
- Python won’t generate any error messages regarding this issue.
Good:
|
1 2 3 4 5 6 7 8 |
try: y = 1 / 0 except ZeroDivisionError: print("Zero Division!") except ArithmeticError: print("Arithmetic problem!") print("THE END.") |
Wrong:
|
1 2 3 4 5 6 7 8 |
try: y = 1 / 0 except ArithmeticError: print("Arithmetic problem!") except ZeroDivisionError: print("Zero Division!") print("THE END.") |
8. The Python statement raise ExceptionName can raise an exception on demand. The same statement, but lacking ExceptionName, can be used inside the try branch only, and raises the same exception which is currently being handled.
The instruction enables you to:
- simulate raising actual exceptions (e.g., to test your handling strategy)
- partially handle an exception and make another part of the code responsible for completing the handling (separation of concerns).
|
1 2 3 4 5 6 7 8 9 |
def bad_fun(n): raise ZeroDivisionError try: bad_fun(0) except ArithmeticError: print("What happened? An error?") print("THE END.") |
What happened? An error?
THE END.
The raise instruction may also be utilized in the following way (note the absence of the exception’s name):
|
1 |
raise |
There is one serious restriction: this kind of raise instruction may be used inside the except branch only; using it in any other context causes an error.
The instruction will immediately re-raise the same exception as currently handled.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
def bad_fun(n): try: return n / 0 except: print("I did it again!") raise try: bad_fun(0) except ArithmeticError: print("I see!") print("THE END.") |
I did it again!
I see!
THE END.
Without rise:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
def bad_fun(n): try: return n / 0 except: print("I did it again!") try: bad_fun(0) except ArithmeticError: print("I see!") print("THE END.") |
I did it again!
THE END.
9. The Python statement assert expression
- evaluates the expression;
- if the expression evaluates to True, or a non-zero numerical value, or a non-empty string, or any other value different than None, it won’t do anything else;
- otherwise, it automatically and immediately raises an exception named
AssertionError(in this case, we say that the assertion has failed)
|
1 2 3 4 5 6 7 8 |
import math x = float(input("Enter a number: ")) assert x >= 0.0 x = math.sqrt(x) print(x) |
Enter a number: 2
1.4142135623730951
Enter a number: -1
Traceback (most recent call last):
File "main.py", line 4, in <module>
assert x >= 0.0
AssertionError
Built-in exceptions
BaseException
Location: BaseException
The most general (abstract) of all Python exceptions – all other exceptions are included in this one; It can be said that the following two except branches are equivalent: except: and except BaseException:.
ArithmeticError
Location: BaseException ← Exception ← ArithmeticError
An abstract exception including all exceptions caused by arithmetic operations like zero division or an argument’s invalid domain.
AssertionError
Location: BaseException ← Exception ← AssertionError
Description: a concrete exception raised by the assert instruction when its argument evaluates to False, None, 0, or an empty string
|
1 2 3 4 5 6 |
from math import tan, radians angle = int(input('Enter integral angle in degrees: ')) # We must be sure that angle != 90 + k * 180 assert angle % 180 != 90 print(tan(radians(angle))) |
LookupError
Location: BaseException ← Exception ← LookupError
An abstract exception including all exceptions caused by errors resulting from invalid references to different collections (lists, dictionaries, tuples, etc.)
IndexError
Location: BaseException ← Exception ← LookupError ← IndexError
A concrete exception raised when you try to access a non-existent sequence’s element (e.g., a list’s element)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# The code shows an extravagant way # of leaving the loop. the_list = [1, 2, 3, 4, 5] ix = 0 do_it = True while do_it: try: print(the_list[ix]) ix += 1 except IndexError: do_it = False print('Done') |
KeyError
Location: BaseException ← Exception ← LookupError ← KeyError
A concrete exception raised when you try to access a collection’s non-existent element (e.g., a dictionary’s element)
|
1 2 3 4 5 6 7 8 9 10 11 12 |
# How to abuse the dictionary # and how to deal with it? dictionary = { 'a': 'b', 'b': 'c', 'c': 'd' } ch = 'a' try: while True: ch = dictionary[ch] print(ch) except KeyError: print('No such key:', ch) |
KeyboardInterrupt
Location: BaseException ← KeyboardInterrupt
A concrete exception raised when the user uses a keyboard shortcut designed to terminate a program’s execution (Ctrl-C in most OSs); if handling this exception doesn’t lead to program termination, the program continues its execution.
Note: this exception is not derived from the Exception class. Run the program in IDLE.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# This code cannot be terminated # by pressing Ctrl-C. from time import sleep seconds = 0 while True: try: print(seconds) seconds += 1 sleep(1) except KeyboardInterrupt: print("Don't do that!") |
MemoryError
Location: BaseException ← Exception ← MemoryError
A concrete exception raised when an operation cannot be completed due to a lack of free memory.
|
1 2 3 4 5 6 7 8 9 10 11 |
# This code causes the MemoryError exception. # Warning: executing this code may affect your OS. # Don't run it in production environments! string = 'x' try: while True: string = string + string print(len(string)) except MemoryError: print('This is not funny!') |
OverflowError
Location: BaseException ← Exception ← ArithmeticError ← OverflowError
A concrete exception raised when an operation produces a number too big to be successfully stored
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# The code prints subsequent # values of exp(k), k = 1, 2, 4, 8, 16, ... from math import exp ex = 1 try: while True: print(exp(ex)) ex *= 2 except OverflowError: print('The number is too big.') |
ImportError
Location: BaseException ← Exception ← StandardError ← ImportError
A concrete exception raised when an import operation fails.
|
1 2 3 4 5 6 7 8 9 |
# One of these imports will fail - which one? try: import math import time import abracadabra except: print('One of your imports has failed.') |
Some useful exceptions
ZeroDivisionError
This appears when you try to force Python to perform any operation which provokes division in which the divider is zero, or is indistinguishable from zero. Note that there is more than one Python operator which may cause this exception to raise. Can you guess them all?
Yes, they are: /, //, and %.
ValueError
Expect this exception when you’re dealing with values which may be inappropriately used in some context. In general, this exception is raised when a function (like int() or float()) receives an argument of a proper type, but its value is unacceptable.
TypeError
This exception shows up when you try to apply a data whose type cannot be accepted in the current context. Look at the example:
|
1 2 |
short_list = [1] one_value = short_list[0.5] |
You’re not allowed to use a float value as a list index (the same rule applies to tuples, too).
TypeError is an adequate name to describe the problem, and an adequate exception to raise.
AttributeError
This exception arrives – among other occasions – when you try to activate a method which doesn’t exist in an item you’re dealing with. For example:
|
1 2 3 |
short_list = [1] short_list.append(2) short_list.depend(3) |
The third line of our example attempts to make use of a method which isn’t contained in the lists. This is the place where
AttributeError is raised.
SyntaxError
This exception is raised when the control reaches a line of code which violates Python’s grammar. It may sound strange, but some errors of this kind cannot be identified without first running the code. This kind of behavior is typical of interpreted languages – the interpreter always works in a hurry and has no time to scan the whole source code. It is content with checking the code which is currently being run. An example of such a category of issues will be presented very soon.
It’s a bad idea to handle this exception in your programs. You should produce code that is free of syntax errors, instead of masking the faults you’ve caused.
IndexError
Look at the code in the editor. What will happen when you run it?
|
1 2 |
my_list = [] x = my_list[0] |
You will see the following message in reply:
Traceback (most recent call last):
File "lst.py", line 2, in
x = list[0]
IndexError: list index out of range
Exercise 1
What is the output of the following program if the user enters 0?
|
1 2 3 4 5 6 7 8 9 |
try: value = int(input("Enter a value: ")) print(value/value) except ValueError: print("Bad input...") except ZeroDivisionError: print("Very bad input...") except: print("Booo!") |
The program will output: Very bad input....
Ex. 2
Assuming the user enters 2 as input, what is the output of the following code snippet?
|
1 2 |
x = input("Enter a number: ") print(x / 0) |
The code will raise a TypeError exception
- The correct answer is
TypeError. ATypeErrorexception is raised whenever an operation is performed on an incorrect data type. In our example, theinputfunction assigns the value2, entered by the user, into the variablex, as a string:x = '2'.
- When evaluating the next
printstatement, the Python interpreter will raise aTypeErrorexception, since the division operator does not support string-type operands. In this case, the interpreter raises theTypeErrorexception before theZeroDivisionErrorexception:print('2' / 0)->TypeError.
Ex. 3
What is the output of the following code snippet?
Ex. 4
What is the output of the following code snippet?
|
1 2 |
for i in range(0, 5, 0): print(i) |
Ex. 6
What is the output of the following code snippet?
|
1 2 3 4 |
s = "python" s[0] = 'P' print(s) |
A TypeError exception
Ex. 7
Which of the following code snippets causes a SyntaxError exception?
Ex. 8
What is the output of the following code snippet?
|
1 2 |
tpl = (1, 2, 3, 4) tpl.append(5) |
The code will raise the ValueError exception
A SyntaxError exception
Exercise 14
What is the expected output of the following code?
|
1 2 3 4 5 6 7 8 |
try: print(1/0) except ZeroDivisionError: print("zero") except ArithmeticError: print("arith") except: print("some") |
zero
Exercise 15
What is the expected output of the following code?
|
1 2 3 4 5 6 7 8 |
try: print(1/0) except ArithmeticError: print("arith") except ZeroDivisionError: print("zero") except: print("some") |
arith
Exercise 16
What is the expected output of the following code?
|
1 2 3 4 5 6 7 8 9 10 |
def foo(x): assert x return 1/x try: print(foo(0)) except ZeroDivisionError: print("zero") except: print("some") |
some


