Skip to content
CS-506 · Python/Quick Revision Short Notes

Python (CS-506) - Unit 4 Short Notes

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 else defeats its purpose. else is for code that should run only when the try block succeeds.

Raising Exceptions

  • Use raise to trigger exceptions manually.

  • Built-in types: raise ValueError("Invalid input"), raise TypeError.

  • Custom Exceptions: Inherit from Exception or 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 OriginalException to explicitly chain:

    
    try:
    
        int("abc")
    
    except ValueError as e:
    
        raise RuntimeError("Conversion failed") from e
    
    

    This 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

  1. 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}")
    
    
  2. contextlib.contextmanager decorator:

    
    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 yield to 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 (raises StopIteration at end).

    • gen.send(value): Send value into generator (resumes at yield expression).

    • 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 chain vs product, and difference between permutations (order matters) and combinations (order doesn’t).


4.5 Modules, Packages, and Distribution

Modules

  • A .py file 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\activate

    Unix/macOS: source myenv/bin/activate

  • pip & requirements.txt:

    pip install -r requirements.txt installs 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') or root = 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”. Use re.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.path for 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/--help generation.

  • action="store_true": Boolean flag (default False).

  • type=int, choices=[1,2,3], default=value are 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, Pipe for inter-process communication.

Asynchronous Programming (asyncio - Conceptual)

  • Use Case: High-concurrency I/O operations (many simultaneous connections).

  • Keywords: async def defines coroutine; await pauses 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 discover finds test_*.py files.

  • Common Assertions: assertEqual, assertTrue, assertFalse, assertIsNone, assertIn.

Debugging Techniques

  • pdb: Insert import pdb; pdb.set_trace() to start interactive debugger.

    • Commands: n (next), s (step into), c (continue), p var (print).
  • Logging: Replace print() with logging.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_case for functions/variables, CamelCase for 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_name generates text documentation.

  • Sphinx: Generates HTML/PDF docs from docstrings (used by major projects).

Packaging for Distribution

  • setup.py (legacy) or pyproject.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_wheel creates distributable archives.

[!TIP]

Always include requirements.txt (generated via pip freeze > requirements.txt) for reproducible environments.

Go to where you left off?

Quick Add to Notes

Save questions, your own notes and screenshots into notes filed by unit. It takes a free account.

Create free account

Have an account? Log in