Python - Exceptions Handling

Hello, future Python wizards! I'm thrilled to be your guide on this exciting journey into the world of Python exception handling. As someone who's been teaching programming for years, I can tell you that understanding exceptions is like learning to ride a bike with training wheels - it might seem tricky at first, but once you get the hang of it, you'll be cruising smoothly in no time!

Python - Exceptions

What is an Exception?

Before we dive into the deep end, let's start with the basics. Imagine you're baking a cake (stick with me here, I promise this relates to Python!). You've got your recipe (your code), your ingredients (your data), and you're ready to go. But what happens if you run out of eggs halfway through? That's an exception in the baking world!

In Python, an exception is an error that occurs during the execution of a program. It's like a red flag that pops up saying, "Whoa there! Something's not quite right!"

Let's look at a simple example:

print(10 / 0)

If you run this code, Python will raise a ZeroDivisionError. It's saying, "Hey, you can't divide by zero! That's not allowed in my kitchen... I mean, programming environment!"

Exception Handling in Python

Now that we know what exceptions are, let's learn how to handle them gracefully. In Python, we use the try and except blocks to catch and handle exceptions.

Here's the basic structure:

try:
    # Code that might raise an exception
except ExceptionType:
    # Code to handle the exception

Let's modify our previous example:

try:
    result = 10 / 0
    print(result)
except ZeroDivisionError:
    print("Oops! You can't divide by zero.")

When you run this code, instead of crashing, it will print: "Oops! You can't divide by zero."

The except Clause with No Exceptions

Sometimes, you might want to catch any exception that occurs. In this case, you can use an except clause without specifying an exception type:

try:
    # Some risky code
    x = int(input("Enter a number: "))
    y = 10 / x
    print(y)
except:
    print("Something went wrong!")

This will catch any exception that occurs in the try block. However, it's generally better to catch specific exceptions when you can, as it makes your code more precise and easier to debug.

The except Clause with Multiple Exceptions

You can also handle multiple exceptions in a single except clause:

try:
    x = int(input("Enter a number: "))
    y = 10 / x
    print(y)
except (ValueError, ZeroDivisionError):
    print("Invalid input or division by zero!")

This code will catch both ValueError (if the user enters a non-numeric value) and ZeroDivisionError.

The try-finally Clause

The finally clause is used for code that must be executed regardless of whether an exception occurs or not. It's like the cleanup crew after a party - it always shows up, no matter how the party went!

try:
    f = open("example.txt", "r")
    # Do some operations on the file
except FileNotFoundError:
    print("The file doesn't exist!")
finally:
    f.close()  # This will always execute, even if an exception occurs

Argument of an Exception

Exceptions can carry additional information. You can access this information in your except block:

try:
    x = 10 / 0
except ZeroDivisionError as e:
    print(f"An error occurred: {e}")

This will print: "An error occurred: division by zero"

Raising an Exception

Sometimes, you might want to raise an exception yourself. It's like being the referee in a game and calling a foul:

def check_age(age):
    if age < 0:
        raise ValueError("Age cannot be negative!")
    print(f"Your age is {age}")

try:
    check_age(-5)
except ValueError as e:
    print(e)

This will print: "Age cannot be negative!"

User-Defined Exceptions

You can also create your own exception types. It's like inventing a new rule in a game:

class TooManyPizzasError(Exception):
    pass

def order_pizza(number):
    if number > 100:
        raise TooManyPizzasError("You can't order more than 100 pizzas!")
    print(f"Order for {number} pizzas placed successfully.")

try:
    order_pizza(101)
except TooManyPizzasError as e:
    print(e)

Assertions in Python

Assertions are a way to ensure that certain conditions are met. They're like little checkpoints in your code:

def divide(a, b):
    assert b != 0, "Divisor cannot be zero!"
    return a / b

try:
    result = divide(10, 0)
except AssertionError as e:
    print(e)

This will print: "Divisor cannot be zero!"

Standard Exceptions

Python comes with a set of built-in exceptions. Here are some of the most common ones:

Exception Name Description
ZeroDivisionError Raised when division or modulo by zero occurs
ValueError Raised when a function gets an argument of correct type but improper value
TypeError Raised when an operation or function is applied to an object of inappropriate type
NameError Raised when a local or global name is not found
FileNotFoundError Raised when a file or directory is requested but doesn't exist
IndexError Raised when a sequence subscript is out of range
KeyError Raised when a dictionary key is not found
ImportError Raised when an import statement fails

And there you have it, folks! You've just completed your crash course in Python exception handling. Remember, exceptions are not your enemies - they're more like helpful signposts guiding you towards better, more robust code. Keep practicing, keep experimenting, and soon you'll be handling exceptions like a pro. Happy coding!

Credits: Image by storyset