Lecture 41: File Concepts — Paths (Absolute vs.
Relative)
This lecture introduces file path fundamentals, a critical prerequisite for file handling, data
processing, automation, and error-free program execution. Many runtime errors in Python
programs arise not from logic, but from incorrect path usage.
1. What is a File Path?
Definition
A file path specifies the exact location of a file or directory in a file system.
It answers two questions:
1. Where is the file stored?
2. How can the operating system reach it?
Example:
C:\Users\Student\Documents\[Link]
2. Components of a File Path
A file path generally contains:
1. Root / Drive
2. Directory hierarchy
3. File name
4. File extension
Example:
/home/user/project/data/[Link]
3. Absolute Path
Definition
An absolute path gives the complete location of a file starting from the root directory.
Key characteristic:
It is independent of the program’s execution location.
Examples
Windows
C:\Users\Admin\Desktop\project\[Link]
Linux / macOS
/home/admin/project/[Link]
Python Example
file = open("C:/Users/Admin/Desktop/project/[Link]", "r")
✔ Always points to the same file
Not portable across systems
4. Relative Path
Definition
A relative path specifies a file’s location relative to the current working directory (CWD).
Key characteristic:
Its meaning depends on where the program is executed.
Examples
Assume current working directory:
/project
Relative Path Meaning
[Link] File in current directory
data/[Link] File inside data folder
../config/[Link] One directory up
Python Example
file = open("data/[Link]", "r")
✔ Portable
✔ Recommended for projects
Fails if CWD is misunderstood
5. Current Working Directory (CWD)
Definition
The current working directory is the directory from which the Python program is executed,
not where it is saved.
Checking CWD in Python
import os
print([Link]())
This is the reference point for resolving relative paths.
6. Absolute vs Relative Paths (Comparison)
Aspect Absolute Path Relative Path
Starting point Root directory Current working directory
Portability Low High
Length Long Short
Dependency on CWD No Yes
Preferred in projects ✔
7. Path Separators and OS Differences
Windows
C:\Users\Student\[Link]
⚠ Backslash is an escape character in Python.
Correct usage:
"C:\\Users\\Student\\[Link]"
OR
r"C:\Users\Student\[Link]"
Linux / macOS
/home/student/[Link]
Uses forward slash / without escape issues.
8. Platform-Independent Paths ([Link])
Best Practice
import os
path = [Link]("data", "[Link]")
Benefits:
• Cross-platform compatibility
• Cleaner code
• Avoids slash confusion
9. Common Errors (Must Be Explicitly Addressed)
1. Confusing script location with CWD
2. Hardcoding absolute paths
3. Incorrect slash usage on Windows
4. Forgetting escape characters
5. Running code from IDE vs terminal inconsistently
Typical error:
FileNotFoundError: No such file or directory
10. Engineering & Academic Use Cases
• Reading CSV files for analysis
• Loading configuration files
• Writing simulation outputs
• Managing project folder structures
• Automating batch file operations
11. Recommended Project Structure (Illustrative)
project/
│
├── data/
│ └── [Link]
├── src/
│ └── [Link]
├── output/
│ └── [Link]
Access from src/[Link]:
file = open("../data/[Link]", "r")
12. Best Practices (Exam & Industry)
1. Prefer relative paths in projects
2. Always verify the current working directory
3. Use [Link]() for portability
4. Avoid hardcoded system-specific paths
5. Clearly document directory structure
13. Summary (Exam-Ready Points)
• File paths define file locations
• Absolute paths start from root
• Relative paths depend on CWD
• Relative paths improve portability
• OS path separators differ
• [Link] ensures cross-platform code
• Incorrect paths cause FileNotFoundError
Lecture 42: File Access Modes and Opening/Closing
This lecture explains how Python opens files, what permissions are granted, and why closing
files correctly is critical. Improper use of file modes is one of the most common causes of data
loss and runtime errors in beginner and intermediate Python programs.
This lecture builds directly on Lecture 41 (Paths: Absolute vs Relative).
1. Opening a File in Python
Basic Syntax
file_object = open(filename, mode)
Where:
• filename → file path (absolute or relative)
• mode → file access mode
• Returns a file object
Example:
f = open("[Link]", "r")
2. Why File Access Modes Matter
File modes determine:
• Whether the file is readable or writable
• Whether existing content is preserved or erased
• Whether a file is created if missing
Incorrect mode → runtime error or permanent data loss.
3. Common File Access Modes (Core Exam Content)
3.1 Read Mode ("r")
• Opens file for reading
• File must already exist
• Default mode
f = open("[Link]", "r")
Error if file does not exist:
FileNotFoundError
3.2 Write Mode ("w")
• Opens file for writing
• Creates file if not present
• Overwrites existing content
f = open("[Link]", "w")
⚠ Dangerous mode – previous data is lost.
3.3 Append Mode ("a")
• Opens file for writing at the end
• Preserves existing content
• Creates file if not present
f = open("[Link]", "a")
Used for:
• Logs
• Incremental data storage
3.4 Exclusive Creation Mode ("x")
• Creates a new file
• Fails if file already exists
f = open("[Link]", "x")
Raises:
FileExistsError
4. Text Mode vs Binary Mode (Intro Level)
Text Mode (Default)
open("[Link]", "r")
Binary Mode
open("[Link]", "rb")
open("[Link]", "wb")
Binary mode is used for:
• Images
• Audio/video
• Executables
5. Summary Table of File Modes (Exam-Favourite)
Mode Meaning File Exists? Data Loss
r Read Must exist No
w Write Created/Overwritten ✔ Yes
a Append Created if missing No
x Create Must not exist No
rb Read binary Must exist No
wb Write binary Created/Overwritten ✔ Yes
6. Closing a File (close())
Why Closing is Important
• Frees system resources
• Flushes buffered data to disk
• Prevents file locking
• Ensures data integrity
Syntax
[Link]()
Example:
f = open("[Link]", "r")
content = [Link]()
[Link]()
7. The with Statement (Best Practice)
Problem with Manual Closing
If an exception occurs, close() may never execute.
Solution: Context Manager
with open("[Link]", "r") as f:
content = [Link]()
✔ File closes automatically
✔ Works even if errors occur
✔ Cleaner and safer
8. Common Student Errors (Must Be Corrected)
1. Using "w" instead of "a" unintentionally
2. Forgetting to close files
3. Writing to files opened in "r" mode
4. Reading from files opened in "w" mode
5. Assuming files close automatically
9. Engineering & Academic Use Cases
• Reading experimental datasets
• Writing simulation results
• Logging program execution
• Managing configuration files
• Batch processing scripts
10. Summary (Exam-Ready Points)
• Files are opened using open(filename, mode)
• File modes control read/write behavior
• "w" overwrites existing content
• Files must be closed after use
• with open() ensures safe closing
• Correct mode selection prevents data loss
Lecture 43: Reading and Writing Text to Files
This lecture explains how data moves between memory and files. After learning paths and
modes (Lectures 41 & 42), this lecture focuses on actual file I/O operations used in data
processing, reporting, and automation.
1. Reading Text Files in Python
Python provides three standard methods:
Method Purpose
read() Read entire file
readline() Read one line
readlines() Read all lines into a list
2. read() – Read Entire File
with open("[Link]", "r") as f:
content = [Link]()
print(content)
• Returns a single string
• Suitable only for small files
• Memory intensive for large files
3. readline() – Read One Line at a Time
with open("[Link]", "r") as f:
line1 = [Link]()
line2 = [Link]()
• Includes newline \n
• Useful for sequential reading
4. readlines() – Read All Lines into a List
with open("[Link]", "r") as f:
lines = [Link]()
Output:
['Line 1\n', 'Line 2\n', 'Line 3\n']
• Each line is a list element
• Suitable for moderate file sizes
5. Iterating Over a File (Best Practice)
with open("[Link]", "r") as f:
for line in f:
print([Link]())
✔ Memory efficient
✔ Preferred for large files
6. Writing Text to Files (write())
with open("[Link]", "w") as f:
[Link]("Python File Handling\n")
[Link]("Lecture 43\n")
Notes:
• write() does not add newline automatically
• Returns number of characters written
7. Writing Multiple Lines (writelines())
lines = ["Line 1\n", "Line 2\n", "Line 3\n"]
with open("[Link]", "w") as f:
[Link](lines)
⚠ Newlines must be included explicitly.
8. Append Mode in Writing
with open("[Link]", "a") as f:
[Link]("New entry added\n")
Used for:
• Logs
• Incremental output
• Audit trails
9. Reading vs Writing (Comparison)
Aspect Reading Writing
Mode r w, a
Operation Extract data Store data
Data loss risk No ✔ Yes (w)
Typical use Analysis Reporting
10. Common Student Errors
1. Forgetting newline characters when writing
2. Using read() on large files
3. Mixing read and write modes incorrectly
4. Writing to files opened in "r" mode
5. Not stripping newline characters
11. Engineering & Academic Use Cases
• Reading sensor data
• Writing experiment results
• Processing logs
• Generating reports
• Automating batch outputs
12. Best Practices
1. Always use with open()
2. Prefer line-by-line iteration for large files
3. Explicitly handle newlines
4. Separate file I/O from logic
5. Validate file existence before reading
13. Summary (Exam-Ready Points)
• read() reads entire file as string
• readline() reads one line
• readlines() returns list of lines
• Files are iterable line-by-line
• write() writes strings
• writelines() writes multiple strings
• Proper mode selection is essential
Lecture 44: Processing Files Line-by-Line
This lecture formalizes the industry-standard approach to handling text files—streaming,
line-by-line processing. It is essential for large datasets, log analysis, batch pipelines, and
memory-safe programs. Reading entire files into memory is often unnecessary and risky; line-
by-line processing avoids those pitfalls.
1. Why Line-by-Line Processing?
Problems with loading entire files
• High memory usage
• Slow performance
• Risk of crashes on large files
• Unnecessary data loading
Principle:
Process data incrementally, not all at once.
2. File Objects Are Iterable
In Python, a file object can be used directly in a loop.
with open("[Link]", "r") as f:
for line in f:
print(line)
What happens internally:
• Python reads one line at a time
• Stops automatically at end-of-file (EOF)
✔ Memory efficient
✔ Clean syntax
✔ Preferred method
3. Standard Line-by-Line Template (Must Memorize)
with open("[Link]", "r") as f:
for line in f:
process(line)
Where:
• line is a string
• Each line usually ends with \n
4. Handling Newline Characters
The Issue
print(line)
Often produces extra blank lines.
Reason:
• Each line already contains \n
Solution: Clean the Line
for line in f:
clean_line = [Link]()
print(clean_line)
Methods:
• strip() → removes leading and trailing whitespace
• rstrip() → removes trailing whitespace only
5. Filtering While Reading
Skip Empty Lines
with open("[Link]", "r") as f:
for line in f:
line = [Link]()
if line == "":
continue
print(line)
Skip Comment Lines
with open("[Link]", "r") as f:
for line in f:
line = [Link]()
if [Link]("#"):
continue
print(line)
6. Processing Data Line-by-Line
Numeric Processing Example
with open("[Link]", "r") as f:
for line in f:
value = float([Link]())
print(value * 2)
Applications:
• Sensor readings
• Numerical datasets
• Batch computations
7. Conditional Logic During Processing
with open("[Link]", "r") as f:
for line in f:
if "ERROR" in line:
print([Link]())
Use cases:
• Log scanning
• Pattern detection
• Validation checks
8. Read → Process → Write (Pipeline Pattern)
with open("[Link]", "r") as fin, open("[Link]", "w") as
fout:
for line in fin:
[Link]([Link]())
✔ Streaming pipeline
✔ No full file in memory
✔ Safe and efficient
9. Aggregation While Reading
Counting Lines
count = 0
with open("[Link]", "r") as f:
for _ in f:
count += 1
print(count)
Summing Values
total = 0
with open("[Link]", "r") as f:
for line in f:
total += int([Link]())
print(total)
10. Performance Perspective (Conceptual)
Method Memory Usage Suitability
read() High Small files only
readlines() Medium Medium files
for line in file Low Large files ✔
11. Common Student Errors (Must Be Corrected)
1. Using read() for large files
2. Forgetting to remove newline characters
3. Ignoring empty or malformed lines
4. Mixing reading and writing without clarity
5. Not using with for file handling
Incorrect:
data = [Link]().split("\n") # memory heavy
Correct:
for line in f:
process(line)
12. Engineering & Academic Use Cases
• Log file analysis
• Large CSV/text processing
• Simulation output parsing
• Data cleaning pipelines
• Automation scripts
13. Best Practices (Engineering Discipline)
1. Always process large files line-by-line
2. Use with open() for safety
3. Strip newline characters early
4. Validate each line before processing
5. Keep file I/O separate from logic
6. Use streaming pipelines where possible
14. Summary (Exam-Ready Points)
• File objects are iterable
• Line-by-line processing is memory efficient
• Each line includes newline characters
• strip() / rstrip() clean input lines
• Filtering and aggregation can be done during reading
• Reading and writing can be combined safely
• This is the standard method for large files
Lecture 45: Introduction to Exceptions and Runtime Errors
This lecture introduces why Python programs fail during execution and how such failures are
formally represented as exceptions. Understanding runtime errors is essential before learning
exception handling (try–except), because one must first know what can go wrong, when it
goes wrong, and why it goes wrong.
This lecture is conceptually foundational for:
• File handling (Lectures 41–44)
• Robust program design
• Debugging and error diagnosis
1. What is an Error in a Program?
Definition
An error is a condition that prevents a program from executing correctly.
Python errors are broadly classified into:
1. Syntax Errors
2. Runtime Errors (Exceptions)
2. Syntax Errors vs Runtime Errors
2.1 Syntax Errors
• Detected before execution
• Caused by violations of Python grammar
• Program does not start
Example:
if x > 5
print(x)
Error:
SyntaxError: invalid syntax
Key point:
Syntax errors must be corrected before execution begins.
2.2 Runtime Errors (Exceptions)
• Occur during execution
• Program starts but terminates abruptly
• Triggered by illegal operations or unexpected conditions
Example:
x = 10 / 0
Error:
ZeroDivisionError: division by zero
3. What is an Exception?
Definition
An exception is a runtime error event that disrupts the normal flow of program execution.
Key idea:
Exceptions are Python’s mechanism for reporting runtime failures.
4. Why Runtime Errors Occur
Runtime errors commonly occur due to:
• Invalid user input
• Mathematical errors
• File system issues
• Data type mismatches
• Invalid indexing or key access
In real-world programs, runtime errors are inevitable.
5. Common Runtime Errors (Exam-Critical)
5.1 ZeroDivisionError
Occurs when division by zero is attempted.
x = 10 / 0
5.2 ValueError
Occurs when a function receives an argument of correct type but invalid value.
int("abc")
5.3 TypeError
Occurs when an operation is applied to incompatible data types.
"10" + 5
5.4 IndexError
Occurs when accessing an invalid list index.
lst = [1, 2, 3]
lst[5]
5.5 KeyError
Occurs when accessing a missing dictionary key.
d = {"a": 1}
d["b"]
5.6 FileNotFoundError
Occurs when attempting to open a non-existent file.
open("[Link]", "r")
6. Program Flow When a Runtime Error Occurs
Program starts
|
Normal execution
|
Runtime error occurs
|
Program terminates (if unhandled)
Important:
Once an exception occurs, all remaining statements are skipped unless the error is handled.
7. Demonstration of Unhandled Runtime Error
print("Start")
x = int(input("Enter number: "))
y = 10 / x
print("Result:", y)
print("End")
If input is 0:
• Program crashes
• "End" is never printed
8. Understanding Error Messages (Traceback)
Typical Python error message:
Traceback (most recent call last):
File "[Link]", line 3, in <module>
x = 10 / 0
ZeroDivisionError: division by zero
Key components:
1. File name
2. Line number
3. Statement causing error
4. Exception type
5. Error description
Students must learn to read tracebacks carefully, not ignore them.
9. Errors vs Bugs (Conceptual Clarity)
• Bug: A logical mistake in program design
• Error/Exception: A failure during execution
A program may be bug-free yet still crash due to:
• Invalid input
• External resource failure
10. Preventing vs Handling Runtime Errors
Prevention (Limited)
if x != 0:
y = 10 / x
Limitations:
• Cannot predict all failure conditions
• Makes code cluttered
Handling (Robust)
• Use exception handling (try–except)
• Handles unexpected failures safely
(Handled in the next lecture.)
11. Common Student Mistakes (Must Be Corrected)
1. Confusing syntax errors with runtime errors
2. Ignoring error messages
3. Assuming input will always be valid
4. Allowing programs to crash silently
5. Treating runtime errors as rare events
12. Engineering & Academic Relevance
Runtime errors are common in:
• File-based data processing
• Numerical computations
• Automation scripts
• User-interactive programs
• Scientific simulations
Professional programs must:
• Anticipate failure
• Detect errors
• Respond gracefully
13. Best Practices (Engineering Discipline)
1. Expect runtime errors in real data
2. Read traceback messages carefully
3. Identify the exact failing line
4. Separate risky operations from logic
5. Prepare code for invalid inputs and missing files
14. Summary (Exam-Ready Points)
• Syntax errors occur before execution
• Runtime errors occur during execution
• Exceptions represent runtime errors in Python
• Unhandled exceptions terminate programs
• Common exceptions include ValueError, TypeError, IndexError
• Traceback provides detailed error information
• Understanding runtime errors is essential before handling them
Lecture 46: Handling Exceptions — try, except, and raise
This lecture converts the understanding of runtime errors (Lecture 45) into controlled,
structured error handling. The objective is to ensure that Python programs do not crash
abruptly, protect resources, and communicate failures meaningfully. Exception handling is a
core professional skill, not an optional feature.
1. Why Exception Handling is Required
Without exception handling:
• Programs terminate unexpectedly
• Users receive cryptic error messages
• Files and resources may remain open
• Data integrity can be compromised
Key principle:
A robust program anticipates failure and handles it explicitly.
2. The try–except Mechanism
Purpose
The try–except construct allows Python to attempt risky operations and recover
gracefully if an error occurs.
Basic Syntax
try:
risky_statements
except ExceptionType:
handling_statements
Execution logic:
1. Python executes the try block
2. If no error occurs → except is skipped
3. If an exception occurs → control jumps to the matching except block
3. Simple try–except Example
try:
x = int(input("Enter a number: "))
y = 10 / x
print("Result:", y)
except ZeroDivisionError:
print("Division by zero is not allowed")
Behavior:
• Input 0 → handled safely
• Program continues execution
4. Handling Multiple Exceptions
Using Multiple except Blocks
try:
value = int(input("Enter value: "))
result = 100 / value
except ValueError:
print("Invalid input format")
except ZeroDivisionError:
print("Cannot divide by zero")
Rules (Exam-Critical):
• except blocks are checked top to bottom
• Only the first matching block executes
5. Catching Multiple Exceptions Together
When recovery logic is the same:
try:
x = int(input("Enter number: "))
y = 10 / x
except (ValueError, ZeroDivisionError):
print("Invalid input or division by zero")
Use this approach only when handling is identical.
6. Generic Exception Handling (Use with Caution)
try:
risky_operation()
except Exception:
print("An unexpected error occurred")
⚠ Warning:
• Masks specific errors
• Makes debugging difficult
Best practice: catch specific exceptions whenever possible.
7. Using else with try–except (Best Practice)
Purpose
Execute code only if no exception occurs.
try:
x = int(input("Enter number: "))
y = 10 / x
except (ValueError, ZeroDivisionError):
print("Error occurred")
else:
print("Computation successful:", y)
Benefit:
• Separates normal logic from error-handling logic
8. The raise Statement (Manual Exception Triggering)
Purpose
raise allows the programmer to explicitly generate an exception when an invalid condition is
detected.
Syntax
raise ExceptionType("error message")
Example: Input Validation
age = int(input("Enter age: "))
if age < 0:
raise ValueError("Age cannot be negative")
This:
• Stops execution
• Provides a meaningful error message
• Forces the caller to handle the issue
9. Raising Exceptions Inside Functions (Professional Pattern)
def withdraw(balance, amount):
if amount > balance:
raise ValueError("Insufficient balance")
return balance - amount
try:
withdraw(5000, 8000)
except ValueError as e:
print("Transaction failed:", e)
✔ Keeps business logic clean
✔ Centralizes error handling
10. Re-raising Exceptions
Used when you want to:
1. Partially handle (e.g., log)
2. Propagate the error upward
try:
process_data()
except Exception as e:
print("Logging error:", e)
raise
raise without arguments rethrows the same exception.
11. Exceptions vs Conditional Logic
Exceptions are not substitutes for normal condition checks.
Incorrect:
try:
if x < 0:
raise ValueError
except ValueError:
print("Negative value")
Correct:
if x < 0:
print("Negative value")
Use exceptions for:
• Unpredictable failures
• External resources
• Invalid program states
12. Common Student Errors (Must Be Corrected)
1. Wrapping the entire program in one try block
2. Catching Exception everywhere
3. Using exceptions for regular control flow
4. Suppressing errors silently
5. Writing vague error messages
Incorrect:
except:
pass # hides serious errors
13. Engineering & Academic Use Cases
• File handling (missing files, permission issues)
• User input validation
• Data processing pipelines
• Numerical computations
• Automation and scripting
14. Best Practices (Engineering Discipline)
1. Catch specific exceptions
2. Keep try blocks minimal
3. Use else for success logic
4. Use raise for invalid states
5. Never suppress exceptions silently
6. Write meaningful error messages
15. Summary (Exam-Ready Points)
• try encloses risky code
• except handles exceptions
• Multiple except blocks are allowed
• Exceptions are matched top-down
• raise explicitly triggers an exception
• else runs only when no exception occurs
• Proper handling prevents crashes and data loss
Lecture 47: Detailed Exception Nuances and Catching Multiple Errors
This lecture advances exception handling from basic recovery to precise, disciplined, and
scalable error management. The focus is on exception hierarchy, ordering rules, catching
multiple exceptions correctly, and propagation behavior—all of which are essential for
writing maintainable, production-grade Python code.
This lecture builds on:
• Lecture 45 → Runtime errors and exceptions
• Lecture 46 → try, except, raise
1. Why Exception Nuances Matter
Basic exception handling prevents crashes.
Nuanced exception handling prevents hidden bugs.
Without understanding nuances:
• Serious errors get masked
• Debugging becomes impossible
• Programs behave incorrectly but appear “stable”
Key principle:
Catch only what you can handle, and handle it meaningfully.
2. Python Exception Hierarchy (Core Concept)
Python exceptions follow an inheritance hierarchy.
Simplified hierarchy:
BaseException
└── Exception
├── ArithmeticError
│ └── ZeroDivisionError
├── ValueError
├── TypeError
├── IndexError
├── KeyError
└── OSError
└── FileNotFoundError
Exam-Critical Rule
Always catch specific exceptions before general ones.
3. Ordering of except Blocks
Incorrect Ordering (Logical Error)
try:
x = 10 / 0
except Exception:
print("General error")
except ZeroDivisionError:
print("Divide by zero")
Why this is wrong:
• Exception catches everything
• ZeroDivisionError is never reached
Correct Ordering
try:
x = 10 / 0
except ZeroDivisionError:
print("Divide by zero")
except Exception:
print("General error")
4. Catching Multiple Exceptions in a Single Block
Syntax
except (ExceptionType1, ExceptionType2):
handling_code
Example
try:
x = int(input("Enter number: "))
y = 100 / x
except (ValueError, ZeroDivisionError):
print("Invalid input or division by zero")
When to Use
✔ Same recovery logic
✖ Different corrective actions required
5. Multiple except Blocks (Preferred Pattern)
try:
value = int(input("Enter value: "))
result = 100 / value
except ValueError:
print("Input must be an integer")
except ZeroDivisionError:
print("Cannot divide by zero")
except Exception:
print("Unexpected error occurred")
Benefits:
• Clear intent
• Precise feedback
• Easier debugging
6. Capturing Exception Details (as e)
Purpose
Access the exception message and metadata.
try:
open("[Link]", "r")
except FileNotFoundError as e:
print("Error details:", e)
Use cases:
• Logging
• Debugging
• User-friendly error reporting
7. Exception Propagation (Call Stack Behavior)
If an exception is not handled, it propagates upward.
def read_file():
return open("[Link]", "r")
def process():
read_file()
process()
If file is missing:
• Exception propagates to the caller
• Program terminates unless caught
Handling at Higher Level
try:
process()
except FileNotFoundError:
print("File missing at application level")
Design rule:
Handle exceptions at the level where recovery decisions are made.
8. Re-raising Exceptions (raise without arguments)
Used when you want to:
1. Log the error
2. Still let it propagate
try:
risky_operation()
except Exception as e:
print("Logging error:", e)
raise
✔ Original traceback preserved
✔ Error visibility maintained
9. Exception Chaining (raise ... from ...)
Purpose
Preserve root cause context.
try:
int("abc")
except ValueError as e:
raise RuntimeError("Data conversion failed") from e
Benefits:
• Clear causal relationship
• Better debugging
• Professional error reporting
10. Avoiding Over-Broad Exception Handling
Dangerous Pattern
try:
risky_code()
except Exception:
pass
Why this is harmful:
• Hides real bugs
• Produces silent failures
• Breaks debugging workflows
Correct approach:
except Exception as e:
print("Unexpected error:", e)
raise
11. Exceptions vs Normal Control Flow
Exceptions should not replace conditional logic.
Incorrect:
try:
if x < 0:
raise ValueError
except ValueError:
print("Negative value")
Correct:
if x < 0:
print("Negative value")
Use exceptions for:
• External failures
• Invalid program states
• Unexpected conditions
12. Common Student Errors (Must Be Explicitly Corrected)
1. Catching Exception everywhere
2. Wrong ordering of except blocks
3. Grouping unrelated exceptions
4. Suppressing exceptions silently
5. Ignoring exception messages
13. Engineering & Academic Use Cases
• File-processing pipelines
• Data ingestion and validation
• Numerical simulations
• Automation scripts
• Large-scale Python applications
14. Best Practices (Engineering Discipline)
1. Catch specific exceptions first
2. Group exceptions only when handling is identical
3. Use as e for diagnostics
4. Re-raise exceptions when necessary
5. Never hide exceptions silently
6. Keep try blocks minimal and focused
15. Summary (Exam-Ready Points)
• Python exceptions follow a hierarchy
• Order of except blocks matters
• Multiple exceptions can be caught together
• Exception objects provide diagnostic details
• Unhandled exceptions propagate up the call stack
• raise rethrows exceptions
• Exception chaining preserves root causes
• Over-broad exception handling is dangerous
Lecture 49: User-defined Exception Classes
This lecture completes the exception-handling module by introducing user-defined (custom)
exception classes. Custom exceptions allow programmers to express domain-specific failures,
enforce business rules, and build clean, maintainable, and scalable Python systems. This is a
defining practice of professional-grade code.
This lecture builds on:
• Runtime errors and exceptions
• try, except, raise
• Multiple exception handling and nuances
• Cleanup using finally
1. Why User-defined Exceptions Are Necessary
Built-in exceptions (e.g., ValueError, TypeError) are generic and often ambiguous.
Example (poor clarity):
raise ValueError("Invalid balance")
Questions left unanswered:
• What rule was violated?
• Is this a business error or a technical error?
User-defined exceptions solve this by adding semantic meaning.
Key principle:
Use custom exceptions to represent domain or business rule violations, not low-level technical
failures.
2. What is a User-defined Exception?
Definition
A user-defined exception is a custom exception class created by inheriting from Python’s
Exception class.
class MyError(Exception):
pass
Characteristics:
• Inherits from Exception
• Raised using raise
• Caught using except like built-in exceptions
3. Creating a Custom Exception Class
Basic Form
class InvalidInputError(Exception):
pass
With Custom Message Handling
class InvalidAgeError(Exception):
def __init__(self, message):
super().__init__(message)
Naming rules (Exam-Critical):
1. Use PascalCase
2. End with Error
3. Name should describe what went wrong
4. Raising a User-defined Exception
Example: Input Validation
class NegativeAgeError(Exception):
pass
age = int(input("Enter age: "))
if age < 0:
raise NegativeAgeError("Age cannot be negative")
Effect:
• Program stops at the exact violation
• Error message clearly states the rule broken
5. Catching User-defined Exceptions
try:
age = int(input("Enter age: "))
if age < 0:
raise NegativeAgeError("Invalid age entered")
except NegativeAgeError as e:
print("Validation error:", e)
✔ Detection and handling are cleanly separated
✔ Error communication is precise
6. Custom Exceptions Inside Functions (Professional Pattern)
class InsufficientBalanceError(Exception):
pass
def withdraw(balance, amount):
if amount > balance:
raise InsufficientBalanceError("Withdrawal exceeds
balance")
return balance - amount
try:
withdraw(5000, 8000)
except InsufficientBalanceError as e:
print("Transaction failed:", e)
Why this is important:
• Functions remain logic-focused
• Error handling remains external
• Code becomes reusable and testable
7. Custom Exception Hierarchies (Advanced but Important)
Custom exceptions can form their own hierarchy, mirroring real systems.
class ApplicationError(Exception):
pass
class ValidationError(ApplicationError):
pass
class DataError(ApplicationError):
pass
Usage:
raise ValidationError("Invalid input data")
Catching:
except ApplicationError:
print("Application-level error")
Benefits:
• Grouped handling
• Cleaner architecture
• Scalable design
8. Exception Chaining with Custom Exceptions
Preserving Root Cause
class DataConversionError(Exception):
pass
try:
int("abc")
except ValueError as e:
raise DataConversionError("Failed to convert data") from e
✔ Original error preserved
✔ Debugging becomes easier
✔ Professional error reporting
9. When to Use User-defined Exceptions
Use when:
• Enforcing business rules
• Modeling domain-specific failures
• Designing libraries or APIs
• Clear distinction from technical errors is required
Do NOT use when:
• A built-in exception already fits perfectly
• The condition is trivial
• Normal control flow is sufficient
10. User-defined Exceptions vs Error Codes
Aspect Error Codes Custom Exceptions
Readability Low High
Debugging Difficult Clear
Flow control Manual Automatic
Pythonic style ✔
Python strongly favors exceptions over return-code checking.
11. Common Student Errors (Must Be Corrected)
1. Not inheriting from Exception
2. Using vague names (MyError, TestError)
3. Raising custom exceptions for trivial logic
4. Catching custom exceptions with generic Exception
5. Writing logic inside exception classes
Incorrect:
class Error:
pass # Not an exception
Correct:
class Error(Exception):
pass
12. Engineering & Academic Use Cases
• Banking and transaction systems
• Validation frameworks
• Data ingestion pipelines
• Scientific computation constraints
• APIs and libraries
• Configuration and policy enforcement
13. Best Practices (Engineering Discipline)
1. Name exceptions after the failure condition
2. Inherit from Exception, not BaseException
3. Keep exception classes lightweight
4. Raise exceptions where the error occurs
5. Handle exceptions at appropriate abstraction levels
6. Document custom exceptions clearly
14. Summary (Exam-Ready Points)
• User-defined exceptions represent domain-specific errors
• Created by inheriting from Exception
• Raised using raise
• Caught like built-in exceptions
• Improve clarity, robustness, and maintainability
• Support custom exception hierarchies
• Essential for professional Python development