4.1 Exception Handling & Robust Programming
The try, except, else, finally Construct
A structured approach to handle runtime errors.
| Clause | Purpose | Execution Timing |
|---|---|---|
try |
Enclose code that might raise an exception | Always executed first |
except |
Handle specific or generic exceptions | Executes if matching exception occurs |
else |
Execute code only if no exception occurred in try block |
Runs after try if no exception |
finally |
Execute cleanup code regardless of exception occurrence | Always runs last (even after return) |
Order of Execution: try → (except | else) → finally
[!TIP]
Common Pitfall: Placing code that can raise an exception inside
elsedefeats its purpose.elseis for code that should run only when thetryblock succeeds.
Raising Exceptions
-
Use
raiseto trigger exceptions manually. -
Built-in types:
raise ValueError("Invalid input"),raise TypeError. -
Custom Exceptions: Inherit from
Exceptionor a subclass.class InsufficientFundsError(Exception): pass raise InsufficientFundsError("Balance too low")
Exception Chaining and Context
-
Python automatically links exceptions (
__context__for implicit,__cause__for explicit). -
Use
raise NewException from OriginalExceptionto explicitly chain:try: int("abc") except ValueError as e: raise RuntimeError("Conversion failed") from eThis preserves the original traceback, aiding debugging.
4.2 File Input/Output (I/O) and Data Persistence
File Modes & Opening Files
open(filename, mode='r', encoding=None)
| Mode | Meaning | Use Case |
|---|---|---|
'r' |
Read (default) | Read existing file |
'w' |
Write (truncate) | Create/overwrite file |
'a' |
Append | Add to end of file |
'x' |
Exclusive creation | Fail if file exists |
'b' |
Binary mode | Non-text (images, executables) |
't' |
Text mode (default) | Text files |
'+' |
Update (read + write) | r+, w+, a+ |
[!TIP]
Always specify
encoding='utf-8'for text files to avoid platform-dependent defaults.
Reading & Writing Methods
-
read(size=-1): Read size bytes (all if -1). -
readline(): Read one line (including newline). -
readlines(): Read all lines into a list. -
write(str): Write string to file. -
writelines(list_of_strings): Write iterable of strings (no newlines added automatically). -
Iteration:
for line in file_obj:is memory-efficient for large files.
CSV Files (csv module)
import csv
with open('data.csv', 'r') as f:
reader = csv.reader(f) # Returns list per row
for row in reader:
print(row)
File Positions
-
tell(): Returns current file pointer position (byte offset). -
seek(offset, whence=0): Move pointer.-
whence=0: From start (default) -
whence=1: From current position -
whence=2: From end
-
4.3 Context Managers (The with Statement)
Purpose: Automate resource setup/teardown (e.g., file closing, lock release).
How it works:
Object must define __enter__() (returns resource) and __exit__() (handles cleanup).
with open('file.txt', 'r') as f:
data = f.read() # f is automatically closed after block
Creating Custom Context Managers
-
Class-based:
class Timer: def __enter__(self): import time; self.start = time.time() def __exit__(self, exc_type, exc_val, exc_tb): print(f"Elapsed: {time.time()-self.start}") -
contextlib.contextmanagerdecorator:from contextlib import contextmanager @contextmanager def managed_file(name): f = open(name, 'w') try: yield f finally: f.close()
4.4 Functional Programming Tools
Decorators
-
Concept: Function that takes a function and returns a modified function.
-
Syntax:
def decorator(func): def wrapper(*args, **kwargs): # Do something before result = func(*args, **kwargs) # Do something after return result return wrapper @decorator def my_func(): ... -
With Arguments:
def repeat(n): def decorator(func): def wrapper(*args, **kwargs): for _ in range(n): func(*args, **kwargs) return wrapper return decorator @repeat(3) def greet(): print("Hello") -
functools.wraps: Use inside wrapper to preserve original function’s name/docstring:from functools import wraps def decorator(func): @wraps(func) def wrapper(*args, **kwargs): ... -
Built-ins:
@staticmethod,@classmethod,@property.
Generators & yield
-
Generator Function: Uses
yieldto produce values lazily.def count_up_to(n): i = 0 while i < n: yield i i += 1 -
Generator Expression:
(x*2 for x in range(10))(like list comp but lazy). -
Memory Efficiency: Generates one value at a time, ideal for large/streaming data.
-
Advanced Methods:
-
next(gen): Get next value (raisesStopIterationat end). -
gen.send(value): Send value into generator (resumes atyieldexpression). -
gen.throw(exc): Raise exception inside generator. -
gen.close(): Terminate generator.
-
itertools Module
| Category | Functions |
|---|---|
| Infinite | count(start=0, step=1), cycle(iterable), repeat(elem, n=None) |
| Finite | chain(*iterables), compress(data, selectors), dropwhile(pred, seq) |
| Combinatoric | product(*iterables, repeat=1), permutations(iterable, r=None) |
combinations(iterable, r), combinations_with_replacement(iterable, r) |
[!TIP]
Exam Focus: Know when to use
chainvsproduct, and difference betweenpermutations(order matters) andcombinations(order doesn’t).
4.5 Modules, Packages, and Distribution
Modules
-
A
.pyfile is a module. -
Import Variants:
import module # Use module.func() from module import func # Use func() directly import module as m # Alias -
sys.path: List of directories Python searches for modules. Current directory is first. -
if __name__ == "__main__":Code inside runs only when file is executed directly (not when imported).
Packages
-
Directory containing
__init__.py(can be empty). -
Absolute Import:
from package.sub import module -
Relative Import:
from .sibling import func(only within package). -
Subpackages: Nested directories with
__init__.py.
Virtual Environments (venv)
-
Isolate project dependencies.
-
Create:
python -m venv myenv -
Activate:
Windows:
myenv\Scripts\activateUnix/macOS:
source myenv/bin/activate -
pip&requirements.txt:pip install -r requirements.txtinstalls all listed packages.
4.6 Data Serialization Formats
JSON (json module)
-
Core Functions:
-
json.dump(obj, file): Write JSON to file. -
json.dumps(obj): Return JSON string. -
json.load(file): Read JSON from file. -
json.loads(str): Parse JSON string.
-
-
Custom Encoding/Decoding:
json.dumps(obj, default=lambda o: o.__dict__) # For custom objects json.loads(str, object_hook=lambda d: MyClass(**d))
XML (xml.etree.ElementTree)
-
Parse:
tree = ET.parse('file.xml')orroot = ET.fromstring(xml_str) -
Create:
elem = ET.Element('tag'); elem.text = 'value' -
Note: XML is more verbose; JSON is preferred for most modern APIs.
4.7 Regular Expressions (re module)
Pattern Fundamentals
-
Raw Strings: Always use
r"pattern"to avoid escaping backslashes. -
Metacharacters:
. ^ $ * + ? [] | () -
Special Sequences:
-
\d: digit,\w: word char,\s: whitespace -
\D,\W,\S: negations -
\b: word boundary
-
Core Functions
| Function | Matches... |
|---|---|
re.search() |
First occurrence anywhere in string |
re.match() |
Only at beginning of string |
re.fullmatch() |
Entire string must match pattern |
re.findall() |
All non-overlapping matches (as list) |
re.finditer() |
Iterator of Match objects |
re.sub() |
Replace matches with replacement string |
re.split() |
Split string by pattern |
Compiling & Flags
-
pattern = re.compile(r"\d+", re.IGNORECASE) -
Common Flags:
-
re.IGNORECASE(re.I): Case-insensitive. -
re.MULTILINE(re.M):^and$match line start/end. -
re.DOTALL(re.S):.matches newline.
-
[!TIP]
Common Pitfall:
re.match()≠ “matches anywhere”. Usere.search()for substring matches.
4.8 System and OS Interaction
os & os.path
-
File/Dir Ops:
os.listdir(),os.mkdir(),os.remove(),os.rename(). -
Path Manipulation (use
os.pathfor portability):-
os.path.join(a, b): Platform-correct path concatenation. -
os.path.split(path):(head, tail) -
os.path.splitext(path):(root, ext) -
os.path.basename(),os.path.dirname() -
os.path.exists(),os.path.isfile(),os.path.isdir()
-
-
Environment:
os.environ(dict-like) for env vars.
sys Module
-
sys.argv: List of command-line arguments ([0]is script name). -
sys.platform: OS identifier ('win32','linux','darwin'). -
Standard Streams:
sys.stdin,sys.stdout,sys.stderr. -
sys.exit([arg]): Exit program (arg can be int status or message).
4.9 Command-Line Argument Parsing
argparse Module
import argparse
parser = argparse.ArgumentParser(description="My script")
parser.add_argument("file", help="Input file path") # Positional
parser.add_argument("-o", "--output", help="Output file") # Optional
parser.add_argument("-v", "--verbose", action="store_true") # Flag
args = parser.parse_args()
-
Automatic
-h/--helpgeneration. -
action="store_true": Boolean flag (defaultFalse). -
type=int,choices=[1,2,3],default=valueare common parameters.
4.10 Logging
Logging Levels (severity increasing):
DEBUG < INFO < WARNING < ERROR < CRITICAL
Basic Configuration:
import logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
filename='app.log' # Omit for stderr
)
-
Logger Objects:
logger = logging.getLogger(__name__)Use hierarchical loggers (e.g.,
'myapp.module'). -
Handlers & Formatters:
handler = logging.FileHandler('debug.log')formatter = logging.Formatter('%(message)s')handler.setFormatter(formatter)logger.addHandler(handler)
[!TIP]
Never use
print()for production diagnostics. Logging provides levels, output control, and persistence.
4.11 Concurrency and Parallelism (Introduction)
Threading (threading)
-
Use Case: I/O-bound tasks (network, disk).
-
GIL (Global Interpreter Lock): Only one thread executes Python bytecode at a time → no true parallelism for CPU-bound tasks.
-
Basic Usage:
import threading t = threading.Thread(target=func, args=(arg1,)) t.start() t.join() # Wait for completion -
Thread Safety: Shared data requires locks (
threading.Lock).
Multiprocessing (multiprocessing)
-
Use Case: CPU-bound tasks (bypasses GIL).
-
Each process has its own Python interpreter & memory space.
-
Basic Usage:
from multiprocessing import Process p = Process(target=func, args=(arg1,)) p.start() p.join() -
IPC: Use
Queue,Pipefor inter-process communication.
Asynchronous Programming (asyncio - Conceptual)
-
Use Case: High-concurrency I/O operations (many simultaneous connections).
-
Keywords:
async defdefines coroutine;awaitpauses until result ready. -
Event Loop: Runs coroutines cooperatively (single-threaded).
-
Not for CPU-bound work (would block loop).
4.12 Testing and Debugging
Unit Testing with unittest
import unittest
class TestMath(unittest.TestCase):
def test_add(self):
self.assertEqual(1+1, 2)
def test_raises(self):
with self.assertRaises(ValueError):
int("abc")
if __name__ == "__main__":
unittest.main()
-
Discovery:
python -m unittest discoverfindstest_*.pyfiles. -
Common Assertions:
assertEqual,assertTrue,assertFalse,assertIsNone,assertIn.
Debugging Techniques
-
pdb: Insertimport pdb; pdb.set_trace()to start interactive debugger.- Commands:
n(next),s(step into),c(continue),p var(print).
- Commands:
-
Logging: Replace
print()withlogging.debug(). -
IDE Debuggers: Breakpoints, step-through, variable inspection (PyCharm, VSCode).
4.13 Code Quality and Packaging for Distribution
PEP 8 (Style Guide)
-
Key Rules: 4-space indentation,
snake_casefor functions/variables,CamelCasefor classes, max line length 79. -
Tools:
-
pylint/flake8: Linting (detect errors/style violations). -
black: Auto-formatter (enforces consistent style).
-
Documentation
-
Docstrings (PEP 257): Triple-quoted string right after
def/class.Conventions: Google, NumPy, reStructuredText.
-
pydoc:pydoc module_namegenerates text documentation. -
Sphinx: Generates HTML/PDF docs from docstrings (used by major projects).
Packaging for Distribution
-
setup.py(legacy) orpyproject.toml(modern): Metadata, dependencies, entry points. -
Basic
setup.py:from setuptools import setup, find_packages setup( name="mypackage", version="0.1", packages=find_packages(), install_requires=["requests>=2.0"] ) -
Build & Install:
pip install .(from project root) installs locally.python setup.py sdist bdist_wheelcreates distributable archives.
[!TIP]
Always include
requirements.txt(generated viapip freeze > requirements.txt) for reproducible environments.