Exceptions Handling
- Concept: A mechanism to intercept runtime errors, prevent the program from crashing, and execute alternative logic or cleanup routines.
- Methods & Syntax:
try:The block of code where you anticipate an error might occur.except ExceptionType:The block that executes only if that specific error occurs.else:Executes only if thetryblock succeeds without any exceptions.finally:Executes always, regardless of success or failure. Strictly used for resource cleanup (e.g., closing files, dropping database connections).
Program:
def safe_divide(a, b): try: result = a / b except ZeroDivisionError: print("Error: Cannot divide by zero.") else: # Executes only if division succeeded print(f"Success! Result is {result}") finally: # Executes no matter what happened above print("Execution of safe_divide completed.\\n")
safe_divide(10, 2) # Triggers 'else' and 'finally'safe_divide(10, 0) # Triggers 'except' and 'finally'Catching & Inspecting Exceptions
Section titled “Catching & Inspecting Exceptions”- Concept: You can target specific errors, group multiple errors together, and extract the system’s exact error message using an alias.
- Methods & Syntax:
except ValueError as exc:Catches aValueErrorand stores the error object in the variableexc.except (Exception1, Exception2):Catches multiple specific exceptions in a single block using a tuple.except Exception as e:The “Catch-All”. Catches almost any programmatic error. (Best practice: Put this at the very bottom of your except blocks as a last resort).
Program:
def temp_convert(var): try: return int(var) except (TypeError, ValueError) as argument: # Extracts the exact string reason the conversion failed print(f"Conversion failed. System reason: {argument}")
temp_convert("xyz")
# Output:# Conversion failed. System reason: invalid literal for int() with base 10: 'xyz'Raising & Chaining Exceptions
Section titled “Raising & Chaining Exceptions”- Concept: You can manually halt your own program by throwing an error if a specific business logic condition is violated.
- Methods & Syntax:
raise ExceptionName("Message"): Triggers an exception immediately.raise NewException from old_exception: (Crucial Addition) Re-raises a new, clearer exception while preserving the original system traceback so you don’t lose the root cause during debugging.
Program:
def process_level(level): if level < 1: # Manually triggering a standard exception raise ValueError(f"Invalid level! {level} is below minimum.") print(f"Processing level {level}")
try: # Simulating a database failure 1 / 0except ZeroDivisionError as original_error: # Exception Chaining: Translating a confusing system error into a clear business error raise SystemError("Database connection failed during processing") from original_error
# Output of the chained error will show:# ZeroDivisionError: division by zero# The above exception was the direct cause of the following exception:# SystemError: Database connection failed during processingCustom Exceptions
Section titled “Custom Exceptions”- Concept: Standard Python exceptions (
ValueError,TypeError) are often too generic for large applications. You create domain-specific errors by inheriting from Python’s baseExceptionclass. - Methods & Syntax:
class CustomError(Exception):Create a class that inherits fromException(orRuntimeError).- Add an
__init__method to accept custom error messages or error codes.
Program:
# Defining the Custom Exceptionclass NetworkError(Exception): """Raised when a network operation fails.""" def __init__(self, message, code): self.message = message self.code = code # Call the base Exception constructor super().__init__(self.message)
# Utilizing the Custom Exceptiondef connect_to_server(hostname): if hostname == "bad_host": raise NetworkError("Failed to resolve hostname", 404)
try: connect_to_server("bad_host")except NetworkError as e: print(f"Network Alert! Code: {e.code}, Reason: {e.message}")
# Output:# Network Alert! Code: 404, Reason: Failed to resolve hostname