Python Error Handling
Making Your Programs Robust: Understanding Errors and Exceptions
In Python, errors can happen for many reasons—maybe a file doesn't exist, a user enters the wrong input, or you try to divide by zero. If you don't handle these errors, your program will crash. Exception handling lets you deal with errors gracefully, so your program can keep running or fail in a controlled way.
Syntax Errors vs. Exceptions
There are two main types of errors in Python:
- Syntax errors: These happen when Python can't understand your code at all. For example, if you forget a colon or a parenthesis:
# SyntaxError: invalid syntax
if True
    print("Hello!")- Exceptions: These happen while your program is running, even if the code is written correctly. For example, dividing by zero or trying to open a file that doesn't exist:
# ZeroDivisionError
result = 10 / 0
# FileNotFoundError
with open("missing.txt") as f:
    data = f.read()The Basics: try and except
To handle exceptions, use a try block. You put the code that might cause an error inside try, and then use except to catch and handle the error.
try:
    number = int(input("Enter a number: "))
    print(10 / number)
except ZeroDivisionError:
    print("You can't divide by zero!")
except ValueError:
    print("That's not a valid number!")- If the user enters 0, it prints the message for ZeroDivisionError.
- If the user enters something that's not a number, it prints the message for ValueError.
Catching Specific Exceptions
You can catch specific types of exceptions by naming them in the except block. This lets you handle different errors in different ways.
try:
    value = int(input("Enter a number: "))
except ValueError:
    print("Please enter a valid integer.")If you want to catch any exception (not recommended for most cases), you can use a plain except: block. But it's better to be specific so you don't hide bugs.
Multiple except Blocks
You can have several except blocks to handle different types of errors:
try:
    with open("data.txt") as f:
        content = f.read()
    number = int(content)
    print(100 / number)
except FileNotFoundError:
    print("File not found.")
except ValueError:
    print("File does not contain a valid number.")
except ZeroDivisionError:
    print("Number in file is zero!")You can also catch multiple exceptions in one block:
try:
    risky()
except (TypeError, ValueError):
    print("A type or value error occurred.")The else Block
You can add an else block after all the except blocks. The code in else runs only if no exception was raised in the try block.
try:
    n = int(input("Enter a number: "))
except ValueError:
    print("Not a number!")
else:
    print(f"You entered {n}")The finally Block
A finally block will always run, whether or not an exception occurred. It's useful for cleanup actions, like closing files or releasing resources.
try:
    f = open("data.txt")
    # Do something with the file
except FileNotFoundError:
    print("File not found.")
finally:
    print("This runs no matter what.")
    if 'f' in locals() and not f.closed:
        f.close()Raising Exceptions
You can raise exceptions yourself using the raise statement. This is useful if you want to signal that something went wrong in your own code.
def divide(a: float, b: float) -> float:
    if b == 0:
        raise ZeroDivisionError("Cannot divide by zero!")
    return a / b
print(divide(10, 2))  # Output: 5.0
# print(divide(10, 0))  # Raises ZeroDivisionErrorCommon Built-in Exceptions
Here are some exceptions you might see often:
- ValueError: Raised when a function gets an argument of the right type but an inappropriate value.
- TypeError: Raised when an operation is applied to an object of the wrong type.
- ZeroDivisionError: Raised when you divide by zero.
- 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 isn't found.
- NameError: Raised when a variable is not defined.
User-Defined Exceptions
You can create your own exception types by defining a new class that inherits from Exception.
class MyCustomError(Exception):
    pass
def do_something(value: int) -> None:
    if value < 0:
        raise MyCustomError("Negative value not allowed!")
    print(f"Value is {value}")
do_something(5)   # Output: Value is 5
# do_something(-1) # Raises MyCustomErrorSummary
- Syntax errors stop your code from running at all; exceptions happen while your code is running.
- Use tryandexceptto handle exceptions and keep your program running.
- Catch specific exceptions to handle different errors in different ways.
- Use elsefor code that should run only if no error occurred.
- Use finallyfor cleanup that should always happen.
- You can raise exceptions yourself with raise.
- You can define your own exception types by subclassing Exception.