Exceptions & Error Handling
Errors are an inevitable part of programming. A network connection might drop, a user might enter a string where a number was expected, or a file might be missing. In Python, these events are handled through Exceptions.
Instead of your program crashing with a cryptic error message, exception handling allows you to intercept the error, respond gracefully, and keep the application running.
1. Exceptions vs. Syntax Errors
Section titled “1. Exceptions vs. Syntax Errors”Before we can handle errors, we must distinguish between the two main types:
- Syntax Errors: These are “grammar” mistakes in your code (e.g., a missing colon or unmatched parentheses). Python catches these before the program even starts. You cannot “handle” these with
try-except. - Exceptions: These occur during execution (runtime). The code is grammatically correct, but something goes wrong during a specific operation (e.g.,
1 / 0).
2. The Python Philosophy: EAFP vs. LBYL
Section titled “2. The Python Philosophy: EAFP vs. LBYL”There are two primary ways to write “safe” code.
LBYL (Look Before You Leap)
Section titled “LBYL (Look Before You Leap)”This style involves checking every possible failure point before performing an action.
if os.path.exists("data.txt"): with open("data.txt") as f: # process fileelse: # handle missing fileEAFP (Easier to Ask for Forgiveness than Permission)
Section titled “EAFP (Easier to Ask for Forgiveness than Permission)”This is the idiomatic Python way. You assume things will work, but you are prepared to handle the failure.
try: with open("data.txt") as f: # process fileexcept FileNotFoundError: # handle missing fileWhy EAFP? It is often faster and more readable. More importantly, it avoids “Race Conditions” (where a file exists when you check it, but is deleted a millisecond later before you open it).
3. The try-except Block
Section titled “3. The try-except Block”The try block contains the “dangerous” code, while the except block contains the “safety net.”
try: num = int(input("Enter a number: ")) result = 100 / num print(f"Result: {result}")except ValueError: print("Error: That wasn't a valid integer.")except ZeroDivisionError: print("Error: You cannot divide by zero.")except Exception as e: print(f"An unexpected error occurred: {e}")The as e Syntax
Section titled “The as e Syntax”By using except Exception as e, you capture the exception object. This object contains valuable information, such as the error message, which you can log or display.
4. Expanding the Flow: else and finally
Section titled “4. Expanding the Flow: else and finally”A complete exception handler can have four parts.
| Clause | Purpose |
|---|---|
try | The code that might fail. |
except | Runs only if an error occurs. |
else | Runs only if no error occurs in the try block. |
finally | Runs no matter what (even if the program crashes or returns). |
try: f = open("log.txt", "w") f.write("System update...")except IOError: print("Could not write to file.")else: print("Write successful!")finally: f.close() print("File resource closed.")Context: The finally block is essential for “Cleanup” tasks like closing database connections or network sockets, ensuring you don’t leak resources.
5. Raising Exceptions
Section titled “5. Raising Exceptions”You can trigger your own exceptions using the raise keyword. This is useful for enforcing business logic.
def set_age(age): if age < 0: raise ValueError("Age cannot be negative!") return age
try: set_age(-5)except ValueError as e: print(e) # Output: Age cannot be negative!6. Under the Hood: Stack Unwinding
Section titled “6. Under the Hood: Stack Unwinding”When an exception is raised, Python’s execution doesn’t just stop. It begins a process called Stack Unwinding:
- Python looks for a
try-exceptblock in the current function. - If none is found, it “pops” the current function off the call stack and goes back to the caller.
- It repeats this process, climbing up the stack until it finds a handler.
- If it reaches the very top (the global scope) without finding a handler, the program terminates and prints the Traceback.
7. Best Practices
Section titled “7. Best Practices”- Be Specific: Never use a bare
except:. It will catch things you don’t want to catch, likeSystemExit(when you try to quit the program). - Keep Try Blocks Small: Only put the lines of code that might actually raise the exception inside the
tryblock. This prevents you from accidentally catching errors you didn’t intend to. - Don’t Silence Errors: Avoid
except: passunless you have a very specific reason. If you ignore an error, you make debugging nearly impossible later.