Skip to main content

Exceptions


1. Need for Exceptions

Explanation:

Exceptions are a way for Python to handle errors gracefully without crashing the program. They allow developers to "catch" errors and take corrective actions, ensuring the program's robustness and user-friendly behavior.

For example, when a program tries to divide a number by zero, an exception is raised to indicate an error. Without exception handling, the program would terminate unexpectedly.


2. Syntax Errors vs Runtime Errors

Explanation:

  • Syntax Errors: These occur when the Python interpreter cannot understand the code because it violates the rules of the Python language. These errors are detected before the program runs.

  • Runtime Errors: These occur while the program is running, often due to invalid operations like dividing by zero or accessing an undefined variable.

Example:

# Syntax Error Example
if True
print("Missing colon") # This will raise a syntax error

# Runtime Error Example
num = 10 / 0 # This will raise a ZeroDivisionError

3. What is an Exception

Explanation:

An exception is an event that disrupts the normal flow of a program. When an error occurs, Python creates an exception object, which is then handled by the program or terminates the program if not handled.


4. Workflow of an Exception (Normal Flow vs Error Flow)

Normal Flow:

  1. Code executes sequentially.
  2. No interruptions or errors.

Error Flow:

  1. Code encounters an error (exception).
  2. Python raises an exception.
  3. If handled, the program continues to run; otherwise, it terminates.

Diagram (Conceptual):

Start
|
Try Block
|
+---------------------------+
| |
No Exception Exception Occurred
| |
Else Block Except Block
| |
Finally Block (Always Runs) |
| |
+-------------------+
|
End


5. Syntax of an Exception

Explanation:

Python provides a try-except block to handle exceptions. Optionally, you can add else and finally clauses.

Syntax:

try:
# Code that might raise an exception
except ExceptionType as e:
# Code to handle the exception
else:
# Code to execute if no exception occurs
finally:
# Code to execute regardless of whether an exception occurs

6. Understanding Exception Handling Blocks

6.1 The try Block

The try block contains the code that might raise an exception. Python executes this block first. If no exception occurs, the program skips the except block and moves to the else block (if present).

6.2 The except Block

The except block is where you define how to handle specific exceptions. You can catch multiple exception types or use a generic except to catch all exceptions.

Example:

try:
num = int(input("Enter a number: "))
result = 10 / num
except ZeroDivisionError:
print("Cannot divide by zero.")
except ValueError:
print("Invalid input. Please enter a number.")

6.3 The else Block

The else block runs if no exceptions occur in the try block. It is often used for code that should only execute when everything goes as expected.

Example:

try:
num = int(input("Enter a number: "))
result = 10 / num
except ZeroDivisionError:
print("Cannot divide by zero.")
else:
print(f"Result: {result}")

6.4 The finally Block

The finally block contains code that executes regardless of whether an exception occurs or not. It is typically used for cleanup tasks, like closing files or releasing resources.

Example:

try:
file = open("example.txt", "r")
content = file.read()
except FileNotFoundError:
print("File not found.")
finally:
file.close()
print("File closed.")


7. Raising an Exception

Explanation:

You can raise exceptions explicitly using the raise statement. This is useful when you want to enforce constraints or signal an error in your program.

Example:

def check_age(age):
if age < 0:
raise ValueError("Age cannot be negative!")
print("Valid age.")

check_age(-5) # This will raise a ValueError

Sample Code: Handling and Raising Exceptions

# Example of Exception Handling
try:
num = int(input("Enter a number: "))
result = 10 / num
print(f"Result: {result}")
except ZeroDivisionError:
print("Cannot divide by zero. Please try again.")
except ValueError:
print("Invalid input. Please enter a number.")
else:
print("Operation successful!")
finally:
print("End of program.")

# Raising an Exception
try:
check_age(-10)
except ValueError as e:
print(f"Caught an exception: {e}")


# 8. Popular Exception Types in Python

---

## Overview
Python provides a wide range of built-in exceptions to handle various error scenarios. Below are some commonly used exception types, their purposes, and examples to illustrate their usage.

---

## 1. `ZeroDivisionError`
Raised when a number is divided by zero.

### Example:
```python
try:
result = 10 / 0
except ZeroDivisionError as e:
print("Cannot divide by zero:", e)

2. ValueError

Raised when a function receives an argument of the correct type but an inappropriate value.

Example:

try:
num = int("abc") # Invalid conversion
except ValueError as e:
print("ValueError occurred:", e)

3. IndexError

Raised when attempting to access an index that is out of range in a list or other sequence.

Example:

try:
lst = [1, 2, 3]
print(lst[5])
except IndexError as e:
print("IndexError occurred:", e)

4. KeyError

Raised when a dictionary key is not found.

Example:

try:
d = {"a": 1, "b": 2}
print(d["c"])
except KeyError as e:
print("KeyError occurred:", e)

5. FileNotFoundError

Raised when an attempt to open a non-existent file is made.

Example:

try:
with open("non_existent_file.txt", "r") as file:
content = file.read()
except FileNotFoundError as e:
print("FileNotFoundError occurred:", e)

6. TypeError

Raised when an operation is applied to an object of an inappropriate type.

Example:

try:
result = "string" + 5 # Invalid operation
except TypeError as e:
print("TypeError occurred:", e)

7. AttributeError

Raised when an invalid attribute reference is made.

Example:

try:
obj = 5
obj.append(10) # Invalid attribute for int
except AttributeError as e:
print("AttributeError occurred:", e)

8. ImportError

Raised when an import statement fails to find the module or cannot load an attribute from the module.

Example:

try:
import non_existent_module
except ImportError as e:
print("ImportError occurred:", e)

9. NameError

Raised when a variable or function name is not found in the local or global scope.

Example:

try:
print(undefined_variable)
except NameError as e:
print("NameError occurred:", e)

10. OverflowError

Raised when the result of an arithmetic operation is too large to be represented.

Example:

try:
import math
print(math.exp(1000)) # Overflow
except OverflowError as e:
print("OverflowError occurred:", e)

Summary

Understanding these exception types can help in writing robust and error-tolerant Python programs. Each exception provides a clear and actionable way to handle specific error scenarios. You can always refer to the Python Documentation for a comprehensive list of exceptions.

Python Exceptions: Practice Exercises

Exercise 1: Catching Exceptions

Task:

Write a program that asks the user to input two numbers and divides the first by the second. Catch any exception that might occur and print an appropriate error message.

### Starter Code:
try:
num1 = int(input("Enter the first number: "))
num2 = int(input("Enter the second number: "))
# TODO: Perform the division and print the result

except ZeroDivisionError:
# TODO: Handle division by zero

except ValueError:
# TODO: Handle invalid input

Exercise 2: Circumvented except Block

Task:

The program below raises a ValueError but is designed to catch a ZeroDivisionError. Fix the program so the exception is handled correctly.

### Starter Code:
try:
value = int("not_a_number") # This raises a ValueError
except ZeroDivisionError:
print("A division error occurred.")
# TODO: Add the correct exception handling

Exercise 3: Using the raise Statement

Task:

Write a function check_age that raises a ValueError if the input age is less than 18. Call this function with a valid and an invalid age and handle the exception in the calling code.

### Starter Code:

def check_age(age):
# TODO: Raise a ValueError if age is less than 18

try:
check_age(15) # Invalid age
print("Age is valid.")
except ValueError as e:
# TODO: Handle the exception

Exercise 4: Loops and Exceptions

Task:

Write a program that continuously asks the user to input numbers until they enter a valid number. Use exception handling to catch invalid inputs (e.g., strings) and prompt the user again.

### Starter Code:
while True:
try:
# TODO: Prompt user for input and convert to integer

# TODO: Print the valid number and break the loop

except ValueError:
# TODO: Print an error message and continue the loop

Exercise 5: Exception Bugs

Task:

Debug the following code. Identify why the finally block does not execute and fix the issue.

### Starter Code:

try:
file = open("non_existent_file.txt", "r")
content = file.read()
print(content)
# TODO: Add missing exception handling

# TODO: Ensure the file is closed in a finally block

Exercise 6: Exceptions with Data Structures

Task:

Write a program that tries to access a non-existent key in a dictionary. Catch the exception and add the missing key to the dictionary with a default value.

### Starter Code:
my_dict = {"a": 1, "b": 2}
try:
# TODO: Access a key that may not exist

except KeyError:
# TODO: Handle the exception by adding the key with a default value

print(my_dict)

Exercise 7: Multiple Exception Types

Task:

Write a program that handles multiple types of exceptions (e.g., ValueError, ZeroDivisionError). Prompt the user for input and handle errors appropriately.

### Starter Code:
try:
# TODO: Prompt the user for two numbers

# TODO: Perform division and print the result

except ValueError:
# TODO: Handle invalid input

except ZeroDivisionError:
# TODO: Handle division by zero