Python - Raising Exceptions

Hello there, aspiring Pythonistas! Today, we're going to dive into the exciting world of raising exceptions in Python. Don't worry if you're new to programming – I'll guide you through this concept step by step, just like I've done for countless students over my years of teaching. So, grab a cup of your favorite beverage, and let's embark on this Python adventure together!

Python - Raising Exceptions

Raising Exceptions in Python

Imagine you're cooking a delicious meal, but suddenly you realize you're out of a crucial ingredient. What do you do? You'd probably stop cooking and alert everyone about the problem, right? Well, that's exactly what raising exceptions in Python is all about – it's a way to signal that something unexpected or problematic has occurred in your code.

Why Raise Exceptions?

Raising exceptions allows us to:

  1. Indicate errors or unusual situations
  2. Control the flow of our program
  3. Provide meaningful feedback to users or other parts of our code

Let's start with a simple example:

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

try:
    result = divide(10, 0)
except ValueError as e:
    print(f"Oops! An error occurred: {e}")

In this example, we're raising a ValueError when someone tries to divide by zero. Let's break it down:

  1. We define a function divide(a, b) that checks if b is zero.
  2. If b is zero, we raise a ValueError with a custom message.
  3. We use a try-except block to catch the exception and print a friendly message.

When you run this code, you'll see:

Oops! An error occurred: Cannot divide by zero!

Raising Built-in Exceptions

Python comes with a variety of built-in exceptions that we can raise. Here's a table of some common ones:

Exception Description
ValueError Raised when a function receives an argument of the correct type but an inappropriate value
TypeError Raised when an operation or function is applied to an object of an inappropriate type
IndexError Raised when a sequence subscript is out of range
KeyError Raised when a dictionary key is not found
FileNotFoundError Raised when a file or directory is requested but doesn't exist

Let's see some examples:

def check_age(age):
    if not isinstance(age, int):
        raise TypeError("Age must be an integer")
    if age < 0:
        raise ValueError("Age cannot be negative")
    print(f"Your age is {age}")

try:
    check_age("twenty")
except TypeError as e:
    print(f"Type error: {e}")

try:
    check_age(-5)
except ValueError as e:
    print(f"Value error: {e}")

When you run this code, you'll see:

Type error: Age must be an integer
Value error: Age cannot be negative

Raising Custom Exceptions

Sometimes, the built-in exceptions just don't cut it. That's when we create our own custom exceptions! It's like being a chef and creating your own unique recipes.

Creating Custom Exceptions

To create a custom exception, we simply define a new class that inherits from the Exception class:

class TooManyPizzasError(Exception):
    pass

def order_pizza(number):
    if number > 100:
        raise TooManyPizzasError("Whoa! That's too many pizzas to handle!")
    print(f"Order confirmed: {number} pizzas")

try:
    order_pizza(101)
except TooManyPizzasError as e:
    print(f"Order failed: {e}")

In this delicious example:

  1. We define a custom exception TooManyPizzasError.
  2. Our order_pizza function raises this exception if someone orders more than 100 pizzas.
  3. We catch the exception and print a friendly message.

When you run this code, you'll see:

Order failed: Whoa! That's too many pizzas to handle!

Re-Raising Exceptions

Sometimes, you might want to catch an exception, do something with it, and then re-raise it for someone else to handle. It's like passing a hot potato in a game!

Here's how you can do it:

def risky_operation():
    print("Starting risky operation...")
    raise ValueError("Something went wrong!")

def perform_operation():
    try:
        risky_operation()
    except ValueError:
        print("Caught an error, logging it...")
        raise  # Re-raise the exception

try:
    perform_operation()
except ValueError as e:
    print(f"Operation failed: {e}")

In this example:

  1. risky_operation() always raises a ValueError.
  2. perform_operation() catches the exception, logs it, and then re-raises it.
  3. We catch the re-raised exception in the outer try-except block.

When you run this code, you'll see:

Starting risky operation...
Caught an error, logging it...
Operation failed: Something went wrong!

And there you have it, folks! We've covered raising exceptions, built-in exceptions, custom exceptions, and even re-raising exceptions. Remember, exceptions are not your enemies – they're valuable tools that help you write robust and error-resistant code.

As I always tell my students, coding is like learning to ride a bike. You might fall a few times, but each fall (or exception) teaches you something new. Keep practicing, stay curious, and don't be afraid to raise a few exceptions along the way!

Happy coding, and may your exceptions always be intentional! ?✨

Credits: Image by storyset