Exception Handling: Keep Programs Safe and User-Friendly

Introduction

In this chapter, you will learn exception handling in Python, a key skill for writing robust programs. Exceptions happen when something unexpected occurs during execution, such as invalid input or missing files. By handling exceptions properly, your program can fail gracefully instead of crashing.

Prerequisites

  • Python 3.10+ installed
  • Basic understanding of functions, conditions, loops, and input/output
  • Ability to run .py files in terminal or IDE

What Is an Exception

An exception is an error raised while the program is running.

Common examples:

  • ValueError (invalid value conversion)
  • ZeroDivisionError (division by zero)
  • FileNotFoundError (file does not exist)
  • KeyError (missing dictionary key)

Without handling, exceptions stop the program immediately.

1) Basic try / except

Use try for risky code and except for fallback handling.

python
try:
    # Convert user input to integer
    number = int(input("Enter an integer: "))
    print(f"You entered: {number}")
except ValueError:
    # Handle invalid integer input
    print("Invalid input. Please enter a valid integer.")

This prevents user mistakes from crashing the script.

2) Handle Multiple Exception Types

You can catch different exceptions separately.

python
try:
    # Read two numbers
    a = int(input("Enter a: "))
    b = int(input("Enter b: "))
 
    # Risky operation
    print(a / b)
except ValueError:
    print("Input must be integers.")
except ZeroDivisionError:
    print("Division by zero is not allowed.")

This gives users clearer feedback.

3) else and finally

  • else runs when no exception occurs
  • finally always runs (with or without exception)
python
try:
    # Try risky operation
    x = int(input("Enter a number: "))
except ValueError:
    print("Invalid input.")
else:
    # Run only when try succeeds
    print(f"Valid number: {x}")
finally:
    # Always run
    print("Execution finished.")

finally is useful for cleanup tasks (closing files, releasing resources).

Tip

Best Practice

Catch specific exceptions whenever possible.
Avoid broad except Exception unless you have a clear reason.

4) Raise Exceptions Manually

Use raise to trigger exceptions when business rules are violated.

python
def set_age(age):
    # Validate age range
    if age < 0 or age > 130:
        raise ValueError("Age must be between 0 and 130.")
    print(f"Age set to {age}")
 
 
try:
    set_age(200)
except ValueError as e:
    print(e)

This helps enforce input contracts in your functions.

5) Exception Object (as e)

Capture exception details for logs or user feedback.

python
try:
    value = int("abc")
except ValueError as e:
    print(f"Caught error: {e}")

This is useful for debugging and error reporting.

6) Real Mini Example: Score Input Guard

This script safely collects scores and prevents invalid data.

python
# Create score list
scores = []
 
# Collect 3 scores safely
for i in range(1, 4):
    while True:
        try:
            # Read one score
            score = float(input(f"Enter score #{i}: "))
 
            # Validate score range
            if score < 0 or score > 100:
                raise ValueError("Score must be between 0 and 100.")
 
            # Save valid score
            scores.append(score)
            break
        except ValueError as e:
            print(f"Invalid score: {e}")
 
# Print final result
print("Valid scores:", scores)
print(f"Average: {sum(scores) / len(scores):.2f}")

This pattern is common in form validation and data-entry systems.

Warning

Do not silently ignore errors with empty except blocks.
Hidden errors make debugging much harder.

Common Beginner Mistakes

Mistake 1: Catching Too Broadly

Using except: without type can hide important bugs.

Mistake 2: Mixing Too Much Logic Inside try

Keep try blocks focused on truly risky statements.

Mistake 3: Forgetting to Validate After Conversion

Successful conversion does not guarantee business-valid values.

Surprise Practice Challenge

Build a "Safe Calculator":

  1. Ask user for two numbers and one operator (+, -, *, /)
  2. Handle invalid number input
  3. Handle division by zero
  4. Handle unsupported operators with raise ValueError
  5. Keep calculator running until user types exit

If you finish this, you can write beginner-level resilient console applications.

FAQ

Why not just let program crash on errors?

Graceful handling improves user experience and prevents data loss in real applications.

Should I always use finally?

Use finally when cleanup is required. It is not mandatory for every try block.

Is except Exception as e always bad?

Not always, but use it carefully. Prefer specific exception types first.

How do I know which exception type to catch?

Read error messages, Python docs, and test edge cases intentionally.