Error Handling in Python

Try / Except

Why Error Handling?

Run this program:


    num = int(input("Enter a number: "))
    print(f"Double is {num * 2}")

Type "hello" instead of a number. Your program crashes:

ValueError: invalid literal for int() with base 10: 'hello'

Now imagine this is a real app with 10,000 users. One wrong input — entire program crashes. That's a disaster.

Error handling lets your program deal with errors gracefully instead of crashing.


Types of Errors in Python

Before learning how to handle errors, understand the two main types:

1. Syntax Errors — Before program runs

Code is written wrong. Python catches these before even running:

if age >= 18     # SyntaxError: missing colon
print "Hello"    # SyntaxError: Python 2 style

You fix these by correcting your code. Error handling cannot catch syntax errors.

2. Exceptions — While program runs

Code is correct but something goes wrong during execution:

int("hello")          # ValueError
10 / 0                # ZeroDivisionError
open("ghost.txt")     # FileNotFoundError
name                  # NameError — variable doesn't exist

These are what try/except handles.


Basic try/except


    try:
        # code that might cause an error
        num = int(input("Enter a number: "))
        print(f"Double is {num * 2}")
    except:
        # code that runs if ANY error occurs
        print("Something went wrong! Please enter a valid number.")

Now if user types "hello":

Enter a number: hello
Something went wrong! Please enter a valid number.

Program does NOT crash. It handles the error and continues.


Catching Specific Exceptions

Using bare except: catches everything — too broad. Better to catch specific errors:


    try:
        num = int(input("Enter a number: "))
        result = 100 / num
        print(f"Result: {result}")
    except ValueError:
        print("Invalid input — please enter a number, not text")
    except ZeroDivisionError:
        print("Cannot divide by zero!")

Now:

  • Type "hello" → ValueError message
  • Type "0" → ZeroDivisionError message
  • Type "5" → works perfectly, prints 20.0

Common Exception Types

Exception

When it happens

ValueError

Wrong type of value — int("hello")

ZeroDivisionError

Dividing by zero — 10 / 0

FileNotFoundError

File doesn't exist

IndexError

List index out of range — list[99]

KeyError

Dictionary key not found — dict["missing"]

TypeError

Wrong type operation — "hello" + 5

NameError

Variable doesn't exist

AttributeError

Method doesn't exist on object



else and finally

try/except has two optional extra blocks:


    try:
        num = int(input("Enter a number: "))
        result = 100 / num
    except ValueError:
        print("Please enter a valid number")
    except ZeroDivisionError:
        print("Cannot divide by zero")
    else:
        # runs ONLY if no exception occurred
        print(f"Success! Result is {result}")
    finally:
        # runs ALWAYS — whether error or not
        print("Program finished")

Test with different inputs:

Input "hello":

Please enter a valid number
Program finished

Input "0":

Cannot divide by zero
Program finished

Input "5":

Success! Result is 20.0
Program finished

else — success path, runs only when no error finally — cleanup code, runs no matter what. Used for closing files, database connections, etc.


Getting Error Details

You can capture the actual error message:


    try:
        num = int("hello")
    except ValueError as e:
        print(f"Error occurred: {e}")

Output:

Error occurred: invalid literal for int() with base 10: 'hello'

The as e gives you the error object. e contains the error message. Very useful for debugging.


Catching Multiple Exceptions Together


    try:
        num = int(input("Enter number: "))
        result = 100 / num
    except (ValueError, ZeroDivisionError) as e:
        print(f"Input error: {e}")

Group exceptions in a tuple when you want to handle them the same way.


Nested try/except


    try:
        with open("data.txt", "r") as file:
            try:
                num = int(file.read())
                print(f"Number from file: {num}")
            except ValueError:
                print("File contains invalid data")
    except FileNotFoundError:
        print("data.txt not found")

Outer try handles file errors, inner try handles data errors.


Raising Your Own Exceptions

You can throw errors on purpose using raise:


    def set_age(age):
        if age < 0:
            raise ValueError("Age cannot be negative")
        if age > 150:
            raise ValueError("Age cannot be more than 150")
        return age

    try:
        age = set_age(-5)
    except ValueError as e:
        print(f"Invalid age: {e}")

Output:

Invalid age: Age cannot be negative

This is how you enforce rules in your functions. If someone passes invalid data, you raise an error with a clear message.


Real World Example 1 — Bulletproof Input

This is something every real program needs — keep asking until valid input:


    def get_integer(prompt):
        while True:
            try:
                value = int(input(prompt))
                return value
            except ValueError:
                print("Invalid input. Please enter a whole number.")

    def get_positive_integer(prompt):
        while True:
            value = get_integer(prompt)
            if value > 0:
                return value
            print("Please enter a positive number.")

    def get_float(prompt):
        while True:
            try:
                value = float(input(prompt))
                return value
            except ValueError:
                print("Invalid input. Please enter a number.")


    # Now use these safe input functions
    age = get_positive_integer("Enter your age: ")
    salary = get_float("Enter your salary: ")

    print(f"\nAge: {age}")
    print(f"Salary: Rs.{salary:,.2f}")

These helper functions are something you'd keep and reuse in every project. They never crash no matter what the user types.


Real World Example 2 — Safe File Handler


    def read_file(filename):
        try:
            with open(filename, "r") as file:
                return file.read()
        except FileNotFoundError:
            print(f"Error: '{filename}' not found")
            return None
        except PermissionError:
            print(f"Error: No permission to read '{filename}'")
            return None

    def write_file(filename, content):
        try:
            with open(filename, "w") as file:
                file.write(content)
            print(f"Successfully saved to '{filename}'")
            return True
        except PermissionError:
            print(f"Error: No permission to write '{filename}'")
            return False
        except Exception as e:
            print(f"Unexpected error: {e}")
            return False


    content = read_file("notes.txt")
    if content:
        print(content)

    write_file("output.txt", "Hello from Python!")


Real World Example 3 — Calculator with Full Error Handling

Let's upgrade the calculator we built in Stage 3:


    def divide(a, b):
        if b == 0:
            raise ZeroDivisionError("Cannot divide by zero")
        return a / b

    def get_number(prompt):
        while True:
            try:
                return float(input(prompt))
            except ValueError:
                print("Please enter a valid number")

    def calculate():
        print("=== Safe Calculator ===")

        num1 = get_number("Enter first number: ")
        num2 = get_number("Enter second number: ")

        print("\nChoose operation:")
        print("1. Add")
        print("2. Subtract")
        print("3. Multiply")
        print("4. Divide")

        choice = input("Choice: ")

        try:
            if choice == "1":
                result = num1 + num2
            elif choice == "2":
                result = num1 - num2
            elif choice == "3":
                result = num1 * num2
            elif choice == "4":
                result = divide(num1, num2)
            else:
                raise ValueError("Invalid operation selected")

            print(f"\nResult: {result:.4f}")

        except ZeroDivisionError as e:
            print(f"Math error: {e}")
        except ValueError as e:
            print(f"Input error: {e}")
        except Exception as e:
            print(f"Unexpected error: {e}")

    calculate()

This calculator will never crash — every possible error is handled.


Exception Hierarchy

In Python all exceptions are organized in a hierarchy. Exception is the parent of almost all exceptions:

Exception
├── ValueError
├── TypeError
├── ZeroDivisionError
├── FileNotFoundError
├── IndexError
├── KeyError
└── ... and many more

So catching Exception catches almost everything:


    try:
        # some code
    except Exception as e:
        print(f"Something went wrong: {e}")

Use this as a last resort — always try to catch specific exceptions first.


Best Practices

1 — Be specific, not general:


    # Bad — too broad
    except:
        pass

    # Good — specific
    except ValueError:
        print("Invalid value")

2 — Never silently swallow errors:


    # Bad — error is hidden, you'll never know something went wrong
    try:
        risky_operation()
    except:
        pass

    # Good — at least log it
    try:
        risky_operation()
    except Exception as e:
        print(f"Error: {e}")

3 — Keep try blocks small:


    # Bad — too much code in try
    try:
        name = input("Name: ")
        age = int(input("Age: "))
        city = input("City: ")
        process_data(name, age, city)
        save_to_file(name)
    except ValueError:
        pass  # which line caused it?

    # Good — only the risky part
    name = input("Name: ")
    try:
        age = int(input("Age: "))
    except ValueError:
        print("Age must be a number")
    city = input("City: ")


Quick Reference


    try:
        # risky code here
    except ValueError:
        # handle value errors
    except ZeroDivisionError:
        # handle division by zero
    except FileNotFoundError:
        # handle missing files
    except (TypeError, KeyError) as e:
        # handle multiple exceptions
        print(f"Error: {e}")
    except Exception as e:
        # catch all other exceptions
        print(f"Unexpected: {e}")
    else:
        # runs only if NO exception occurred
        print("Success!")
    finally:
        # always runs
        print("Done")

    # Raise your own exception
    raise ValueError("Your custom error message")


Exercise 🏋️

Build a Student Marks Manager with full error handling:

  1. Store student data in a file marks.txt in this format:
Rahul,85,90,78
Priya,92,88,95
Gagan,75,82,79
  1. Build a menu:
=== Marks Manager ===
1. View all students
2. Add student
3. Get student average
4. Delete student
5. Exit

Handle ALL these errors properly:

  • File not found when loading
  • Invalid marks input (non-numbers)
  • Student not found when searching
  • Duplicate student name when adding
  • Invalid menu choice

This exercise combines file handling from Stage 6 with error handling from Stage 7 — two stages in one project!

No comments:

Post a Comment

Error Handling in Python

Try / Except Why Error Handling? Run this program:     num = int ( input ( " Enter a number: " ))     print ( f "Double i...