Chapter 6 Exercises

Comprehensive exercises covering exception handling in Python.

Chapter 6 Exercises

Comprehensive exercises covering exception handling in Python, sorted from easiest to hardest.

Exercises

Exercise 1: Safe List Access

Write a function safe_get that takes a list and an index. Use try-except to handle IndexError. If the index is valid, return the element. If an IndexError occurs, return None.

Safe List Access

Checks: 0 times
Answer:
def safe_get(my_list, index):
try:
return my_list[index]
except IndexError:
return None

Exercise 2: Safe Dictionary Access

Write a function safe_dict_get that takes a dictionary and a key. Use try-except to handle KeyError. If the key exists, return its value. If a KeyError occurs, return the string “Key not found”.

Safe Dictionary Access

Checks: 0 times
Answer:
def safe_dict_get(my_dict, key):
try:
return my_dict[key]
except KeyError:
return "Key not found"

Exercise 3: Integer Conversion with Error Handling

Write a function safe_int that takes a value and tries to convert it to an integer. Handle ValueError and TypeError. If conversion succeeds, return the integer. If it fails, return None.

Integer Conversion with Error Handling

Checks: 0 times
Answer:
def safe_int(value):
try:
return int(value)
except (ValueError, TypeError):
return None

Exercise 4: Division with Multiple Exception Types

Write a function divide_numbers that takes two arguments and divides the first by the second. Handle ZeroDivisionError, TypeError, and ValueError separately, each printing a specific message. Return the result if successful, None otherwise.

Division with Multiple Exception Types

Checks: 0 times
Answer:
def divide_numbers(a, b):
try:
return a / b
except ZeroDivisionError:
print("Cannot divide by zero")
return None
except TypeError:
print("Invalid type for division")
return None
except ValueError:
print("Invalid value for division")
return None

Exercise 5: List Operations with Exception Handling

Write a function process_list that takes a list and an operation (as a string: ‘sum’, ‘max’, ‘min’, ‘average’). Use try-except to handle cases where the list is empty (raise ValueError), contains non-numeric values (handle TypeError), or the operation is invalid (raise ValueError). Return the result of the operation.

List Operations with Exception Handling

Checks: 0 times
Answer:
def process_list(my_list, operation):
try:
if not my_list:
raise ValueError("List is empty")
if operation == 'sum':
return sum(my_list)
elif operation == 'max':
return max(my_list)
elif operation == 'min':
return min(my_list)
elif operation == 'average':
return sum(my_list) / len(my_list)
else:
raise ValueError("Invalid operation")
except ValueError as e:
print(f"Error: {e}")
return None
except TypeError:
print("Error: List contains non-numeric values")
return None

Exercise 6: Nested Try-Except Blocks

Write a function nested_access that takes a nested list of lists and two indices (row, col). Use nested try-except blocks to handle IndexError at both the outer list level and inner list level. In the outer try block, access the row. In the inner try block (inside the outer try), access the column. Return the element if found, None if either index is out of range.

Nested Try-Except Blocks

Checks: 0 times
Answer:
def nested_access(matrix, row, col):
try:
row_list = matrix[row] # Outer try: access row
try:
return row_list[col] # Inner try: access column
except IndexError:
return None # Column index out of range
except IndexError:
return None # Row index out of range

Exercise 7: Exception Handling with Comprehensions

Write a function safe_divide_list that takes two lists of numbers and returns a new list where each element is the division of corresponding elements from the two lists. Use a list comprehension with a helper function that handles ZeroDivisionError and IndexError. If division by zero occurs, use None. If index is out of range, use 0.

Exception Handling with Comprehensions

Checks: 0 times
Answer:
def safe_divide(a, b):
try:
return a / b
except ZeroDivisionError:
return None
except IndexError:
return 0
def safe_divide_list(list1, list2):
def divide_at_index(i):
try:
return safe_divide(list1[i], list2[i])
except IndexError:
return 0
return [divide_at_index(i) for i in range(max(len(list1), len(list2)))]

Exercise 8: Custom Exception for Validation

Write a function validate_age that takes an age value. Try to convert it to an integer first. If conversion fails, raise a TypeError. If the age is negative or greater than 150, raise a ValueError. Then write code that calls this function and handles both exceptions, printing appropriate messages.

Custom Exception for Validation

Checks: 0 times
Answer:
def validate_age(age):
try:
age = int(age)
except (ValueError, TypeError):
raise TypeError("Age must be an integer")
if age < 0:
raise ValueError("Age cannot be negative")
if age > 150:
raise ValueError("Age cannot be greater than 150")
return True
try:
age_input = input()
validate_age(age_input)
print("Valid age")
except TypeError as e:
print(f"TypeError: {e}")
except ValueError as e:
print(f"ValueError: {e}")

Exercise 9: Dictionary Operations with Exception Handling

Write a function dict_operations that takes a dictionary and performs multiple operations: gets a value by key, calculates the sum of all numeric values, and finds the maximum value. Use try-except blocks to handle KeyError, TypeError, and ValueError. Return a dictionary with results or error messages.

Dictionary Operations with Exception Handling

Checks: 0 times
Answer:
def dict_operations(my_dict, key):
result = {}
# Get value by key
try:
result['value'] = my_dict[key]
except KeyError:
result['value'] = 'Key not found'
# Calculate sum
try:
numeric_values = [v for v in my_dict.values() if type(v) == int or type(v) == float]
if numeric_values:
result['sum'] = sum(numeric_values)
else:
result['sum'] = 'Cannot sum non-numeric values'
except (TypeError, ValueError):
result['sum'] = 'Cannot sum non-numeric values'
# Find max
try:
numeric_values = [v for v in my_dict.values() if type(v) == int or type(v) == float]
if numeric_values:
result['max'] = max(numeric_values)
else:
result['max'] = 'Cannot find max of non-numeric values'
except (TypeError, ValueError):
result['max'] = 'Cannot find max of non-numeric values'
return result

Exercise 10: Try-Except-Else-Finally with Functions

Write a function process_data that takes a list of numbers and a divisor. Divide each number by the divisor and return a list of results. Use try-except-else-finally blocks. In the try block, perform all divisions. Handle ZeroDivisionError and TypeError by printing error messages. In the else block, print “All operations successful”. In the finally block, print “Processing complete”. Make sure the else block executes when no exceptions occur.

Try-Except-Else-Finally with Functions

Checks: 0 times
Answer:
def process_data(numbers, divisor):
results = []
try:
for num in numbers:
results.append(num / divisor)
except ZeroDivisionError:
print("Cannot divide by zero")
results = []
except TypeError:
print("Invalid divisor type")
results = []
else:
print("All operations successful")
finally:
print("Processing complete")
return results

Exercise 11: Complex Exception Handling with Comprehensions

Write a function safe_matrix_operations that takes a matrix (list of lists) and an operation (‘sum’, ‘max’, ‘min’). Use dictionary comprehension to create a mapping of row indices to their operation results. Handle IndexError, TypeError, and ValueError. If a row is empty or contains non-numeric values, map it to None.

Complex Exception Handling with Comprehensions

Checks: 0 times
Answer:
def safe_matrix_operations(matrix, operation):
def process_row(row):
try:
if not row:
return None
numeric_values = [x for x in row if type(x) == int or type(x) == float]
if not numeric_values:
return None
if operation == 'sum':
return sum(numeric_values)
elif operation == 'max':
return max(numeric_values)
elif operation == 'min':
return min(numeric_values)
else:
raise ValueError("Invalid operation")
except (TypeError, ValueError):
return None
return {i: process_row(row) for i, row in enumerate(matrix)}

Exercise 12: Exception Handling with Lambda and Map

Write a function safe_map_divide that takes two lists and a divisor. Use map() with a lambda function to divide each element of the first list by the divisor, then divide by corresponding elements from the second list. Handle ZeroDivisionError and IndexError. Return a list where errors are represented as None.

Exception Handling with Lambda and Map

Checks: 0 times
Answer:
def safe_map_divide(list1, list2, divisor):
def safe_divide(x, y):
try:
return (x / divisor) / y
except (ZeroDivisionError, IndexError):
return None
return [safe_divide(list1[i], list2[i]) if i < len(list2) else None for i in range(len(list1))]

Exercise 13: Nested Exception Handling with Functions

Write a function process_nested_data that takes a nested dictionary structure. Extract values from nested dictionaries, handle KeyError at multiple levels, and calculate statistics (sum, average) of numeric values found. Use try-except blocks at different nesting levels. Return a dictionary with statistics or error messages.

Nested Exception Handling with Functions

Checks: 0 times
Answer:
def process_nested_data(data):
all_values = []
for key, nested_dict in data.items():
try:
try:
values = nested_dict['values']
if type(values) == list:
all_values.extend([v for v in values if type(v) == int or type(v) == float])
except KeyError:
pass
except (TypeError, AttributeError):
pass
if all_values:
return {
'sum': sum(all_values),
'average': sum(all_values) / len(all_values),
'count': len(all_values)
}
else:
return {'sum': 0, 'average': 0, 'count': 0}

Exercise 14: Complex Exception Handling with Sorting

Write a function safe_sort_with_key that takes a list of dictionaries and a key function (as a lambda or function). Sort the list using the key function, but handle cases where the key doesn’t exist (KeyError), the value is not sortable (TypeError), or the list is empty. Return the sorted list or None if sorting fails.

Complex Exception Handling with Sorting

Checks: 0 times
Answer:
def safe_sort_with_key(data, key_func):
try:
if not data:
return []
return sorted(data, key=key_func)
except KeyError:
return None
except TypeError:
return None
except Exception:
return None

Exercise 15: Ultimate Exception Handling Challenge

Write a function complex_data_processor that takes a list of mixed data (numbers, strings, lists, dictionaries). Use comprehensions, map, filter, and exception handling to: 1) Extract all numeric values from nested structures, 2) Calculate statistics (sum, max, min, average), 3) Handle all possible exceptions (TypeError, ValueError, KeyError, IndexError, AttributeError). Return a dictionary with results or error messages for each operation.

Ultimate Exception Handling Challenge

Checks: 0 times
Answer:
def extract_numbers(item):
numbers = []
try:
if type(item) == int or type(item) == float:
numbers.append(item)
elif type(item) == list:
for subitem in item:
numbers.extend(extract_numbers(subitem))
elif type(item) == dict:
for value in item.values():
numbers.extend(extract_numbers(value))
except (TypeError, ValueError, KeyError, IndexError, AttributeError):
pass
return numbers
def complex_data_processor(data):
all_numbers = []
for item in data:
all_numbers.extend(extract_numbers(item))
try:
if all_numbers:
return {
'sum': sum(all_numbers),
'max': max(all_numbers),
'min': min(all_numbers),
'average': sum(all_numbers) / len(all_numbers)
}
else:
return {'sum': 0, 'max': None, 'min': None, 'average': 0}
except (TypeError, ValueError):
return {'error': 'Cannot process data'}

Course Progress

Section 48 of 61

Back to Course