<h2>15.1 Introduction to Exception Handling</h2>
<p>
Exception handling is used to manage errors that may occur while a Python
program is running. Instead of allowing the program to stop suddenly,
we can handle certain errors and provide a meaningful message to the user.
</p>
<p>
Python provides <code>try</code>, <code>except</code>, <code>else</code>,
and <code>finally</code> blocks for handling exceptions.
</p>
<pre><code>try:
number = int(input("Enter a number: "))
print(number)
except ValueError:
print("Please enter a valid number.")</code></pre>
<h2>15.2 What is an Exception?</h2>
<p>
An exception is an error that occurs during program execution and
interrupts the normal flow of a program.
</p>
<p>
For example, trying to divide a number by zero produces an exception.
</p>
<pre><code>number = 10
result = number / 0
print(result)</code></pre>
<p>
The above program produces a <code>ZeroDivisionError</code>.
</p>
<h2>15.3 Why is Exception Handling Important?</h2>
<p>
Exception handling makes programs more reliable and user-friendly.
It allows a program to respond to unexpected situations without
crashing unnecessarily.
</p>
<ul>
<li>Prevents sudden program termination</li>
<li>Provides meaningful error messages</li>
<li>Improves program reliability</li>
<li>Helps handle invalid user input</li>
<li>Makes debugging easier</li>
</ul>
<h2>15.4 The try Block</h2>
<p>
The <code>try</code> block contains code that may produce an exception.
</p>
<pre><code>try:
number = int(input("Enter a number: "))
print("You entered:", number)</code></pre>
<p>
If an exception occurs inside the <code>try</code> block, Python looks
for a suitable <code>except</code> block.
</p>
<h2>15.5 The except Block</h2>
<p>
The <code>except</code> block is used to handle an exception.
</p>
<pre><code>try:
number = int(input("Enter a number: "))
except ValueError:
print("Invalid input.")</code></pre>
<h2>15.6 Handling ValueError</h2>
<p>
A <code>ValueError</code> can occur when a function receives a value
of the correct type but an inappropriate value.
</p>
<pre><code>try:
age = int(input("Enter your age: "))
print("Age:", age)
except ValueError:
print("Age must be a number.")</code></pre>
<h2>15.7 Handling ZeroDivisionError</h2>
<pre><code>try:
number = 20
divisor = 0
result = number / divisor
print(result)
except ZeroDivisionError:
print("A number cannot be divided by zero.")</code></pre>
<h2>15.8 Handling IndexError</h2>
<p>
An <code>IndexError</code> occurs when you try to access an invalid
index of a sequence.
</p>
<pre><code>numbers = [10, 20, 30]
try:
print(numbers[5])
except IndexError:
print("Index is outside the list.")</code></pre>
<h2>15.9 Handling KeyError</h2>
<p>
A <code>KeyError</code> can occur when a dictionary key does not exist.
</p>
<pre><code>student = {
"name": "Aman",
"marks": 88
}
try:
print(student["age"])
except KeyError:
print("The requested key does not exist.")</code></pre>
<h2>15.10 Handling TypeError</h2>
<p>
A <code>TypeError</code> can occur when an operation is performed on
incompatible types.
</p>
<pre><code>try:
result = "10" + 5
print(result)
except TypeError:
print("The two values have incompatible types.")</code></pre>
<h2>15.11 Multiple except Blocks</h2>
<p>
A program can use multiple <code>except</code> blocks to handle
different types of exceptions.
</p>
<pre><code>try:
number = int(input("Enter a number: "))
result = 100 / number
print(result)
except ValueError:
print("Please enter a valid number.")
except ZeroDivisionError:
print("Cannot divide by zero.")</code></pre>
<h2>15.12 Using a General Exception</h2>
<p>
The <code>Exception</code> class can be used to catch many common
runtime exceptions.
</p>
<pre><code>try:
number = int(input("Enter a number: "))
print(100 / number)
except Exception as error:
print("An error occurred:", error)</code></pre>
<p>
Specific exceptions are generally preferable when you know what kind
of error you want to handle.
</p>
<h2>15.13 The else Block</h2>
<p>
The <code>else</code> block runs when no exception occurs in the
<code>try</code> block.
</p>
<pre><code>try:
number = int(input("Enter a number: "))
except ValueError:
print("Invalid number.")
else:
print("You entered:", number)</code></pre>
<h2>15.14 try, except and else Together</h2>
<pre><code>try:
number = int(input("Enter a number: "))
result = 100 / number
except ValueError:
print("Please enter a valid integer.")
except ZeroDivisionError:
print("Zero is not allowed.")
else:
print("Result:", result)</code></pre>
<h2>15.15 The finally Block</h2>
<p>
The <code>finally</code> block runs after the <code>try</code> and
<code>except</code> processing, whether or not an exception occurs.
It is commonly used for cleanup operations.
</p>
<pre><code>try:
number = int(input("Enter a number: "))
except ValueError:
print("Invalid input.")
finally:
print("Program finished.")</code></pre>
<h2>15.16 Complete Exception Handling Structure</h2>
<p>
A complete exception-handling structure can contain all four blocks.
</p>
<pre><code>try:
number = int(input("Enter a number: "))
except ValueError:
print("Invalid input.")
else:
print("Number:", number)
finally:
print("Execution completed.")</code></pre>
<h2>15.17 Example with Division</h2>
<pre><code>try:
number = int(input("Enter a number: "))
divisor = int(input("Enter divisor: "))
result = number / divisor
except ValueError:
print("Please enter valid integers.")
except ZeroDivisionError:
print("Divisor cannot be zero.")
else:
print("Result:", result)
finally:
print("Thank you for using the program.")</code></pre>
<h2>15.18 Raising an Exception</h2>
<p>
The <code>raise</code> statement can be used to deliberately generate
an exception when a specific condition is not acceptable.
</p>
<pre><code>age = 15
if age < 18:
raise ValueError("Age must be 18 or above.")</code></pre>
<h2>15.19 Raising a ValueError</h2>
<pre><code>def check_marks(marks):
if marks < 0 or marks > 100:
raise ValueError("Marks must be between 0 and 100.")
return marks
print(check_marks(85))</code></pre>
<h2>15.20 Handling a Raised Exception</h2>
<pre><code>def check_age(age):
if age < 18:
raise ValueError("Age must be 18 or above.")
return True
try:
check_age(16)
except ValueError as error:
print(error)</code></pre>
<h2>15.21 Using Exception Objects</h2>
<p>
You can store the exception object in a variable using the
<code>as</code> keyword.
</p>
<pre><code>try:
number = int("abc")
except ValueError as error:
print("Error:", error)</code></pre>
<h2>15.22 Nested try Blocks</h2>
<p>
A <code>try</code> block can be placed inside another <code>try</code>
block when different levels of error handling are required.
</p>
<pre><code>try:
number = int(input("Enter a number: "))
try:
result = 100 / number
print(result)
except ZeroDivisionError:
print("Cannot divide by zero.")
except ValueError:
print("Invalid input.")</code></pre>
<h2>15.23 Exception Handling with Lists</h2>
<pre><code>numbers = [10, 20, 30]
try:
index = int(input("Enter index: "))
print(numbers[index])
except ValueError:
print("Enter a valid integer index.")
except IndexError:
print("That index does not exist.")</code></pre>
<h2>15.24 Exception Handling with Dictionaries</h2>
<pre><code>student = {
"name": "Aman",
"marks": 88
}
try:
key = input("Enter key: ")
print(student[key])
except KeyError:
print("Key not found.")</code></pre>
<h2>15.25 Exception Handling with Functions</h2>
<p>
Exception handling can be used inside functions to make reusable
program components safer.
</p>
<pre><code>def divide(a, b):
try:
return a / b
except ZeroDivisionError:
return "Cannot divide by zero."
print(divide(20, 4))
print(divide(20, 0))</code></pre>
<p>Output:</p>
<pre><code>5.0
Cannot divide by zero.</code></pre>
<h2>15.26 Returning an Error Message</h2>
<pre><code>def convert_number(value):
try:
return int(value)
except ValueError:
return "Invalid number."
print(convert_number("25"))
print(convert_number("hello"))</code></pre>
<p>Output:</p>
<pre><code>25
Invalid number.</code></pre>
<h2>15.27 Input Validation</h2>
<p>
Exception handling is frequently used to validate information entered
by users.
</p>
<pre><code>while True:
try:
age = int(input("Enter your age: "))
if age < 0:
print("Age cannot be negative.")
continue
break
except ValueError:
print("Please enter a number.")
print("Your age is:", age)</code></pre>
<h2>15.28 Handling Invalid Marks</h2>
<pre><code>try:
marks = float(input("Enter marks: "))
if marks < 0 or marks > 100:
raise ValueError("Marks must be between 0 and 100.")
print("Marks:", marks)
except ValueError as error:
print("Invalid marks:", error)</code></pre>
<h2>15.29 File Handling with Exception Handling</h2>
<p>
Exception handling is especially useful when working with files because
the requested file may not exist.
</p>
<pre><code>try:
with open("student.txt", "r") as file:
content = file.read()
print(content)
except FileNotFoundError:
print("Student file was not found.")</code></pre>
<h2>15.30 File Cleanup with finally</h2>
<pre><code>file = None
try:
file = open("example.txt", "r")
print(file.read())
except FileNotFoundError:
print("File not found.")
finally:
if file is not None:
file.close()
print("File operation completed.")</code></pre>
<h2>15.31 Using finally for Cleanup</h2>
<p>
The <code>finally</code> block is useful for operations that should
happen regardless of whether an exception occurs.
</p>
<pre><code>try:
print("Opening resource")
number = 10 / 2
except ZeroDivisionError:
print("Division error.")
finally:
print("Cleaning up resource.")</code></pre>
<h2>15.32 Custom Exception Classes</h2>
<p>
Python allows you to create your own exception classes by inheriting
from <code>Exception</code>.
</p>
<pre><code>class InvalidMarksError(Exception):
pass
marks = 120
if marks > 100:
raise InvalidMarksError("Marks cannot be greater than 100.")</code></pre>
<h2>15.33 Handling a Custom Exception</h2>
<pre><code>class InvalidMarksError(Exception):
pass
try:
marks = 120
if marks > 100:
raise InvalidMarksError("Invalid marks.")
except InvalidMarksError as error:
print(error)</code></pre>
<h2>15.34 Custom Exception with a Function</h2>
<pre><code>class InsufficientBalanceError(Exception):
pass
def withdraw(balance, amount):
if amount > balance:
raise InsufficientBalanceError("Insufficient balance.")
return balance - amount
try:
balance = withdraw(1000, 1500)
print("Remaining balance:", balance)
except InsufficientBalanceError as error:
print(error)</code></pre>
<h2>15.35 Exception Chaining</h2>
<p>
Sometimes one exception occurs while handling another exception.
Python allows related exceptions to be connected using
<code>from</code>.
</p>
<pre><code>try:
number = int("abc")
except ValueError as error:
raise RuntimeError("Unable to process the number.") from error</code></pre>
<h2>15.36 Assertions</h2>
<p>
The <code>assert</code> statement can be used to check whether a
condition is true during program execution.
</p>
<pre><code>age = 20
assert age >= 18
print("Age is valid.")</code></pre>
<h2>15.37 Assertion Error</h2>
<pre><code>age = 15
assert age >= 18, "Age must be at least 18"
print("Age is valid.")</code></pre>
<h2>15.38 Difference Between Error and Exception</h2>
<table>
<thead>
<tr>
<th>Term</th>
<th>Meaning</th>
</tr>
</thead>
<tbody>
<tr>
<td>Error</td>
<td>A problem that prevents or disrupts normal program execution.</td>
</tr>
<tr>
<td>Exception</td>
<td>An event that occurs during execution and can often be handled.</td>
</tr>
</tbody>
</table>
<h2>15.39 Common Python Exceptions</h2>
<table>
<thead>
<tr>
<th>Exception</th>
<th>Common Cause</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>ValueError</code></td>
<td>Invalid value for an operation</td>
</tr>
<tr>
<td><code>TypeError</code></td>
<td>Incompatible data types</td>
</tr>
<tr>
<td><code>ZeroDivisionError</code></td>
<td>Division by zero</td>
</tr>
<tr>
<td><code>IndexError</code></td>
<td>Invalid sequence index</td>
</tr>
<tr>
<td><code>KeyError</code></td>
<td>Missing dictionary key</td>
</tr>
<tr>
<td><code>FileNotFoundError</code></td>
<td>Requested file does not exist</td>
</tr>
<tr>
<td><code>PermissionError</code></td>
<td>Insufficient permission</td>
</tr>
<tr>
<td><code>NameError</code></td>
<td>Undefined variable or name</td>
</tr>
<tr>
<td><code>AttributeError</code></td>
<td>Invalid attribute access</td>
</tr>
</tbody>
</table>
<h2>15.40 try vs except vs else vs finally</h2>
<table>
<thead>
<tr>
<th>Block</th>
<th>Purpose</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>try</code></td>
<td>Contains code that may raise an exception</td>
</tr>
<tr>
<td><code>except</code></td>
<td>Handles an exception</td>
</tr>
<tr>
<td><code>else</code></td>
<td>Runs when no exception occurs</td>
</tr>
<tr>
<td><code>finally</code></td>
<td>Runs after the exception-handling process</td>
</tr>
</tbody>
</table>
<h2>15.41 Complete Example: Calculator</h2>
<pre><code>try:
first = float(input("Enter first number: "))
second = float(input("Enter second number: "))
operator = input("Enter operator (+, -, *, /): ")
if operator == "+":
result = first + second
elif operator == "-":
result = first - second
elif operator == "*":
result = first * second
elif operator == "/":
result = first / second
else:
raise ValueError("Invalid operator.")
except ValueError as error:
print("Error:", error)
except ZeroDivisionError:
print("Cannot divide by zero.")
else:
print("Result:", result)
finally:
print("Calculator operation completed.")</code></pre>
<h2>15.42 Complete Example: Student Marks</h2>
<pre><code>def get_marks():
try:
marks = float(input("Enter marks: "))
if marks < 0 or marks > 100:
raise ValueError("Marks must be between 0 and 100.")
return marks
except ValueError as error:
print("Error:", error)
return None
marks = get_marks()
if marks is not None:
print("Valid marks:", marks)</code></pre>
<h2>15.43 Complete Example: Safe Division</h2>
<pre><code>def safe_divide(a, b):
try:
result = a / b
except ZeroDivisionError:
return "Cannot divide by zero."
else:
return result
finally:
print("Division operation completed.")
print(safe_divide(20, 5))
print(safe_divide(20, 0))</code></pre>
<h2>15.44 Best Practices for Exception Handling</h2>
<ul>
<li>Catch specific exceptions whenever possible.</li>
<li>Do not use a broad <code>except</code> unnecessarily.</li>
<li>Provide useful error messages.</li>
<li>Use <code>finally</code> for cleanup operations.</li>
<li>Validate user input before processing it.</li>
<li>Use custom exceptions when they improve program clarity.</li>
<li>Do not hide important programming errors.</li>
<li>Keep exception-handling code simple and readable.</li>
</ul>
<h2>15.45 Chapter Summary</h2>
<p>
In this chapter, you learned how Python handles runtime problems using
exception handling. You learned how to detect exceptions, respond to
different errors, validate input, and create custom exceptions.
</p>
<ul>
<li>What an exception is</li>
<li>Why exception handling is useful</li>
<li>The <code>try</code> block</li>
<li>The <code>except</code> block</li>
<li>Multiple exception handlers</li>
<li>The <code>else</code> block</li>
<li>The <code>finally</code> block</li>
<li>The <code>raise</code> statement</li>
<li>Custom exceptions</li>
<li>Assertions</li>
<li>Common Python exceptions</li>
<li>Exception handling with files and functions</li>
</ul>
<h2>15.46 Quick Revision Questions</h2>
<ol>
<li>What is an exception in Python?</li>
<li>Why is exception handling important?</li>
<li>What is the purpose of the <code>try</code> block?</li>
<li>What is the purpose of the <code>except</code> block?</li>
<li>When does the <code>else</code> block execute?</li>
<li>When does the <code>finally</code> block execute?</li>
<li>What is a <code>ValueError</code>?</li>
<li>What is a <code>ZeroDivisionError</code>?</li>
<li>What does the <code>raise</code> statement do?</li>
<li>What is a custom exception?</li>
</ol>
<h2>15.47 Practice Exercises</h2>
<ol>
<li>Write a program that safely converts user input into an integer.</li>
<li>Create a program that handles division by zero.</li>
<li>Write a program that handles invalid list indexes.</li>
<li>Write a program that handles missing dictionary keys.</li>
<li>Create a program that safely opens a file.</li>
<li>Use <code>try</code>, <code>except</code>, and <code>else</code>
in a calculator.</li>
<li>Use <code>finally</code> to display a completion message.</li>
<li>Create a custom exception for invalid marks.</li>
<li>Create a custom exception for an invalid age.</li>
<li>Build a safe student marks input program.</li>
</ol>
<h2>15.48 Mini Project: Safe Student Result Program</h2>
<p>
Create a program that accepts a student's name and marks, validates
the marks, and displays the result without allowing invalid input to
crash the program.
</p>
<pre><code>name = input("Enter student name: ")
try:
marks = float(input("Enter marks: "))
if marks < 0 or marks > 100:
raise ValueError("Marks must be between 0 and 100.")
except ValueError as error:
print("Error:", error)
else:
print("Student:", name)
print("Marks:", marks)
finally:
print("Result processing completed.")</code></pre>
<h2>15.49 What's Next?</h2>
<p>
<strong>
Next Chapter: Python Object-Oriented Programming – Classes and Objects
</strong>
</p>