Exception handling in Python

Exception handling in Python is the process of handling errors or exceptions that occur during the execution of a program. When an exception occurs, the normal execution of the program is interrupted and control is transferred to a special block of code called an exception handler. This allows you to gracefully handle errors and prevent your program from crashing.

In Python, you can handle exceptions using a `try-except` block. Here’s an example:

python
try:
    # Code that might raise an exception
    x = 1 / 0
except ZeroDivisionError:
    # Code to handle the exception
    print("Error: division by zero")

In this example, we have a `try` block that contains code that might raise an exception (in this case, dividing by zero). We then have an `except` block that handles the `ZeroDivisionError` exception by printing an error message.

You can also use multiple `except` blocks to handle different types of exceptions:

python
try:
    # Code that might raise an exception
    file = open("nonexistent_file.txt", "r")
except FileNotFoundError:
    # Code to handle the FileNotFoundError exception
    print("Error: file not found")
except PermissionError:
    # Code to handle the PermissionError exception
    print("Error: permission denied")

In this example, we have a `try` block that tries to open a file that doesn’t exist. We then have two `except` blocks that handle the `FileNotFoundError` and `PermissionError` exceptions, respectively.

You can also use an `else` block to execute code if no exceptions are raised:

python
try:
    # Code that might raise an exception
    x = 1 / 1
except ZeroDivisionError:
    # Code to handle the exception
    print("Error: division by zero")
else:
    # Code to execute if no exceptions are raised
    print("No errors occurred")

In this example, we have an `else` block that executes if no exceptions are raised in the `try` block.

Finally, you can use a `finally` block to execute code regardless of whether an exception is raised or not (for example, to clean up resources like file handles):

python
try:
    # Code that might raise an exception
    file = open("example.txt", "r")
    contents = file.read()
except FileNotFoundError:
    # Code to handle the FileNotFoundError exception
    print("Error: file not found")
finally:
    # Code to execute regardless of whether an exception is raised
    file.close()

In this example, we have a `finally` block that closes the file handle, regardless of whether an exception is raised or not.