Lesson · 40 min · Free
Python Error Handling
Python Error Handling body { font-family: Arial, sans-serif; line-height: 1.6; margin: 20px; } h1, h2 { color: #2c3e50; } pre { background-color: #ecf0f1; padding: 15px; border-radius: 5px; overflow-x: auto; } code { fon
Python Error Handling
Welcome to the Python Error Handling lesson, a crucial component for developing robust and reliable code, especially in sensitive fields like pharmaceutical research and data science. In these domains, an unhandled error can lead to incorrect data analysis, failed experiments, or even compromised patient safety. Therefore, understanding how to anticipate and manage errors gracefully is paramount. Python, like most programming languages, provides mechanisms to deal with errors and exceptional conditions that arise during program execution. These are broadly categorized into two types: syntax errors and exceptions . Syntax errors occur when the Python interpreter cannot understand your code because it violates the language's grammatical rules (e.g., a missing colon or an unclosed parenthesis). These errors prevent the program from running at all and must be fixed before execution. Exceptions, on the other hand, occur during the execution of a program, even if the syntax is correct. These are often due to logical flaws, invalid input, or external factors like network issues or file permissions. Effective error handling allows your program to continue running even when unexpected situations occur, rather than crashing abruptly. This is achieved primarily through the try-except block. The basic idea is to "try" to execute a block of code that might raise an exception. If an exception occurs, the program "catches" it and executes an alternative block of code, allowing for graceful recovery or informative error messages.
Understanding and Implementing try-except Blocks
The try-except statement is the core of Python's error handling. You place the code that might raise an exception inside the try block. If an exception occurs within this block, Python immediately jumps to the corresponding except block. If no exception occurs, the except block is skipped. Consider a scenario where you are processing patient data from a file, and one of the numerical fields might be corrupted (e.g., contains text instead of a number). A direct conversion to an integer would raise a ValueError . Without error handling, your program would crash, potentially halting a critical analysis. With try-except , you can log the error, skip the corrupted record, or assign a default value, ensuring the analysis continues. try: # Attempt to convert a string to an integer patient_id_str = "ABC123" patient_id_int = int(patient_id_str) print(f"Patient ID: {patient_id_int}") except ValueError: # This block executes if a ValueError occurs in the try block print(f"Error: Could not convert '{patient_id_str}' to an integer. Invalid patient ID format.") # In a real-world scenario, you might log this error, # assign a default value, or skip the record. except TypeError: # This block would catch a TypeError, for example, if patient_id_str was None print(f"Error: Patient ID data type is incorrect.") except Exception as e: # This is a general catch-all for any other unexpected exceptions print(f"An unexpected error occurred: {e}") else: # The 'else' block executes if no exceptions were raised in the 'try' block print("Patient ID processed successfully without errors.") finally: # The 'finally' block always executes, regardless of whether an exception occurred or not. # It's often used for cleanup operations, like closing files or database connections. print("Attempted patient ID processing.") print("\n--- Another example with successful execution ---") try: data_value = "12345" numeric_value = int(data_value) result = 100 / numeric_value print(f"Result of division: {result}") except ValueError: print(f"Error: Cannot convert '{data_value}' to a number.") except ZeroDivisionError: print("Error: Cannot divide by zero.") else: print("Operation completed successfully.") finally: print("End of operation attempt.") You can specify multiple except blocks to handle different types of exceptions. It's generally good practice to catch specific exceptions first, followed by more general ones. The else block (optional) executes if the try block completes without raising any exceptions. The finally block (also optional) always executes, regardless of whether an exception occurred or not, making it ideal for cleanup operations like closing files or releasing resources. Python has a rich hierarchy of built-in exceptions. Some common ones you might encounter include: ValueError : Raised when an operation receives an argument that has the right type but an inappropriate value (e.g., int('hello') ). TypeError : Raised when an operation or function is applied to an object of an inappropriate type (e.g., adding a string to an integer without conversion). FileNotFoundError : Raised when a file or directory is requested but doesn't exist. ZeroDivisionError : Raised when the second operand of a division or modulo operation is zero. IndexError : Raised when a sequence subscript is out of range (e.g., accessing an element beyond the list's length). KeyError : Raised when a dictionary key is not found. In data science and pharmacy, these exceptions can manifest in various ways: a missing data file ( FileNotFoundError ), incorrect drug dosage calculation due to a zero divisor ( ZeroDivisionError ), or attempting to access non-existent patient attributes in a dictionary ( KeyError ). Robust error handling ensures that your data pipelines and analytical scripts don't fail unexpectedly, potentially saving significant time and resources, and ensuring data integrity. import os def load_patient_data(filename): """ Attempts to load patient data from a CSV file. Handles FileNotFoundError and potential data conversion errors. """ patient_records = [] try: if not os.path.exists(filename): raise FileNotFoundError(f"The file '{filename}' does not exist.") with open(filename, 'r') as f: header = f.readline().strip().split(',') # Assuming first line is header for line_num, line in enumerate(f, 2): # Start counting from line 2 for data try: data = line.strip().split(',') if len(data) != len(header): raise ValueError(f"Line {line_num} has incorrect number of columns.") # Example: Assuming Age is the second column and should be an integer # And Weight is the third column and should be a float age = int(data[1]) weight = float(data[2]) patient_records.append({ header[0]: data[0], # Patient ID header[1]: age, header[2]: weight }) except ValueError as ve: print(f"Warning: Data error on line {line_num} in '{filename}': {ve}. Skipping record.") except IndexError as ie: print(f"Warning: Index error on line {line_num} in '{filename}': {ie}. Skipping record.") except Exception as e: print(f"Warning: An unexpected error occurred on line {line_num} in '{filename}': {e}. Skipping record.") except FileNotFoundError as fnfe: print(f"Error: {fnfe}") return None except IOError as ioe: print(f"Error reading file '{filename}': {ioe}") return None except Exception as e: print(f"An unhandled error occurred during file processing: {e}") return None else: print(f"Successfully processed data from '{filename}'.") return patient_records finally: print(f"File processing attempt for '{filename}' completed.") # --- Test cases --- # Create a dummy file for demonstration with open("valid_patient_data.csv", "w") as f: f.write("PatientID,Age,Weight\n") f.write("P001,30,75.5\n") f.write("P002,45,80.2\n") with open("invalid_patient_data.csv", "w") as f: f.write("PatientID,Age,Weight\n") f.write("P003,twenty,65.0\n") # Invalid age f.write("P004,50,seventy\n") # Invalid weight f.write("P005,25\n") # Missing column print("--- Loading valid data ---") valid_data = load_patient_data("valid_patient_data.csv") if valid_data: print(f"Loaded {len(valid_data)} valid records.") print(valid_data) print("\n--- Loading invalid data ---") invalid_data = load_patient_data("invalid_patient_data.csv") if invalid_data: print(f"Loaded {len(invalid_data)} records (some with warnings).") print(invalid_data) print("\n--- Trying to load non-existent data ---") non_existent_data = load_patient_data("non_existent_file.csv") if non_existent_data is None: print("Could not load data from non_existent_file.csv as expected.")
Key Takeaways:
Syntax Errors vs. Exceptions: Syntax errors prevent execution; exceptions occur during execution. try-except Block: The fundamental structure for handling runtime errors. Code that might fail goes in try ; recovery logic goes in except . Specific Exception Handling: Catch specific exceptions (e.g., ValueError , FileNotFoundError ) before more general ones to provide targeted error messages and recovery. else Block: Executes if no exceptions are raised in the try block. finally Block: Always executes, useful for cleanup operations (e.g., closing files, releasing resources). Importance in Pharma/Data Science: Essential for building resilient data pipelines, ensuring data integrity, preventing crashes in critical applications, and providing informative feedback when issues arise.
Practice Exercise:
You are developing a script to analyze drug dosage data. The data is stored in a list of dictionaries, where each dictionary represents a patient's record and contains keys like 'patient_id' , 'drug_mg' (dosage in milligrams), and 'weight_kg' (patient weight in kilograms). Your task is to calculate the dosage per kilogram for each patient. Write a Python function called calculate_dosage_per_kg(patient_records) that takes this list of dictionaries as input. The function should iterate through the records and for each patient, attempt to calculate drug_mg / weight_kg . Implement robust error handling: If 'drug_mg' or 'weight_kg' keys are missing from a patient's record, print a warning and skip that patient's calculation. If 'drug_mg' or 'weight_kg' values are not valid numbers (e.g., strings like "unknown"), print a warning and skip. If 'weight_kg' is zero, print a warning about division by zero and skip. For successfully calculated dosages, store them in a new dictionary where the key is patient_id and the value is the calculated dosage per kg, and return this dictionary. Test your function with a sample list of patient records that includes valid data, missing keys, non-numeric values, and zero weight.
Watch the full lesson — free
This topic is part of The Complete Python Bootcamp: Basics to Pharma & Data Science, a complete AI-narrated video course. Press play once and watch the entire lecture like a movie.
Start the course free →