Board Formulas
🐍 PythonπŸ“… Day 16

Exception Handling in Python

Catch runtime errors with try and except, use else and finally, raise your own exceptions, and recognise Python's common built-in exceptions.

🎯

Learning objectives

  • β†’Tell syntax errors apart from exceptions that happen while a program runs
  • β†’Handle errors with try, except, else and finally
  • β†’Catch specific exceptions and read their messages
  • β†’Raise an exception yourself with raise

πŸ’‘ Key points

  • A syntax error stops the program before it starts. An exception happens while it is running, such as dividing by zero.
  • Put the code that might fail inside try; put the recovery code inside except.
  • Catch specific exceptions (except ValueError:). A bare except: also catches mistakes you did not expect and hides real bugs.
  • else runs only when the try block raised no exception; finally runs every time, error or not.
  • except ValueError as e: gives you the exception object; print(e) shows its message.
  • raise ValueError('message') throws an exception yourself, for example to reject impossible data.
  • Common built-ins: ZeroDivisionError, ValueError, TypeError, NameError, IndexError, KeyError, FileNotFoundError.

πŸ’» Code examples(6)

#1try and except
python
a, b = 10, 0
try:
    print(a / b)
except ZeroDivisionError:
    print("Cannot divide by zero")
print("Program continues")
Output
Cannot divide by zero
Program continues
Without try, Python would stop with ZeroDivisionError: division by zero and the last line would never run.
#2Keep asking until the input is valid
python
while True:
    try:
        marks = int(input("Enter marks: "))
        break                 # valid: leave loop
    except ValueError:
        print("Not a number, try again.")
print("You entered", marks)
Output
Enter marks: ninety
Not a number, try again.
Enter marks: 90
You entered 90
Sample run: the user first typed ninety, then 90. int() raises ValueError for text that is not a whole number, so the loop asks again.
#3Several except blocks
python
marks = [87, 92, 76]
for pos in ["1", "x", "5"]:
    try:
        print(marks[int(pos)])
    except ValueError as e:
        print("ValueError:", e)
    except IndexError as e:
        print("IndexError:", e)
Output
92
ValueError: invalid literal for int() with base 10: 'x'
IndexError: list index out of range
Python checks the except blocks from top to bottom and runs the first one that matches. as e lets you print the exact message.
#4else and finally
python
def divide(a, b):
    try:
        result = a / b
    except ZeroDivisionError:
        print("Error: division by zero")
    else:
        print("Result:", result)
    finally:
        print("Done with", a, "/", b)

divide(10, 4)
divide(5, 0)
Output
Result: 2.5
Done with 10 / 4
Error: division by zero
Done with 5 / 0
else holds the code that should run only on success. finally always runs, which makes it the place for clean-up such as closing a file.
#5Raising your own exception
python
def set_marks(m):
    if not 0 <= m <= 100:
        raise ValueError(f"marks must be 0-100, got {m}")
    return m

for m in [88, 105]:
    try:
        print("Saved", set_marks(m))
    except ValueError as e:
        print("Rejected:", e)
Output
Saved 88
Rejected: marks must be 0-100, got 105
raise stops the function immediately. The caller decides what to do, here by catching the error and printing the message.
#6Common built-in exceptions
python
tests = [
    lambda: 10 / 0,
    lambda: int("abc"),
    lambda: "5" + 5,
    lambda: [1, 2][5],
    lambda: {"a": 1}["b"],
    lambda: total_marks,
    lambda: open("missing.txt"),
]
for t in tests:
    try:
        t()
    except Exception as e:
        print(type(e).__name__, "-", e)
Output
ZeroDivisionError - division by zero
ValueError - invalid literal for int() with base 10: 'abc'
TypeError - can only concatenate str (not "int") to str
IndexError - list index out of range
KeyError - 'b'
NameError - name 'total_marks' is not defined
FileNotFoundError - [Errno 2] No such file or directory: 'missing.txt'
Each lambda (Day 9) triggers a different error. except Exception catches almost all of them, which is fine for a demo; in real programs catch the specific type you expect.

🎯 Practice

Q1. Which block always runs, whether an exception happened or not?+

finally.

Q2. What is printed? try: print(int('7') + 1) except ValueError: print('bad') else: print('ok')+

8 and then ok. No exception occurred, so the else block also runs.

Q3. Name the exception raised by: (a) [1, 2, 3][3] (b) int('12a') (c) 5 + 'x' (d) {'a': 1}['z']+

(a) IndexError (b) ValueError (c) TypeError (d) KeyError

Q4. Write a function sqrt_of(n) that raises ValueError if n is negative, otherwise returns n ** 0.5.+

def sqrt_of(n): if n < 0: raise ValueError('n must not be negative') return n ** 0.5

Q5. Why is a bare except: (with no exception name) a bad idea?+

It catches every error, including typos such as a misspelled variable (NameError), so real bugs are hidden. Name the exceptions you expect.

πŸ“ Notes

Reading a traceback

When an exception is not handled, Python prints a traceback and stops. For a file demo.py whose second line is x = 10 / 0, Python 3.12 prints (with the full path of your file in place of demo.py):

Traceback (most recent call last):
  File "demo.py", line 2, in <module>
    x = 10 / 0
        ~~~^~~
ZeroDivisionError: division by zero

Read it from the bottom: the last line names the exception and gives the message, and the lines above it show the file, the line number and (in newer versions) markers under the part that failed.

How the blocks fit together

try:
    ...   # code that might fail
except SomeError:
    ...   # runs only if SomeError happened
else:
    ...   # runs only if nothing went wrong
finally:
    ...   # runs every time
  • try must be followed by at least one except or a finally.
  • You can catch several types in one block: except (ValueError, TypeError):.
  • Order matters: put specific exceptions before general ones like Exception.

Errors you cannot catch this way

A syntax error (a missing colon, an unclosed bracket) is found before the program runs, so try never gets a chance. Logical errors (a wrong formula giving a wrong answer) raise nothing at all. Exception handling only helps with problems that happen at run time.

Common mistakes

  • Too much code inside try: keep only the risky lines there, so you know exactly what failed.
  • Swallowing errors silently: except: pass makes bugs invisible. At least print a message.
  • Catching the wrong type: int("abc") raises ValueError, not TypeError.
  • Using exceptions for normal checks: if b != 0: is clearer than catching ZeroDivisionError every time.

Next: Day 17 β€” Text File Handling, where FileNotFoundError and finally become very useful.