Skip to content
IT-605 ยท Programming in Python/Quick Revision Short Notes

Programming in Python (IT-605) - Unit 5 Short Notes

UNIT 5: Advanced Python Concepts & Application

1.0 Object-Oriented Programming (OOP) in Depth

1.1 Classes and Objects

  • Class: Blueprint/template for creating objects. Defined using class keyword.

  • Object: Specific instance of a class.

  • __init__(self, ...): Constructor method. Automatically called upon instantiation to initialize instance attributes.

    
    class Student:
    
        def __init__(self, name, roll_no):
    
            self.name = name  # Instance attribute
    
            self.roll_no = roll_no
    
    s1 = Student("Alice", 101)  # Instantiation
    
    

1.2 Instance vs. Class Attributes

Feature Instance Attribute Class Attribute
Defined Inside __init__ (using self.) Directly under class definition
Ownership Belongs to individual object Shared across all instances
Example self.name = name total_students = 0
Modification Changes affect only that object Changes affect all objects

1.3 Method Types

Decorator First Parameter Purpose Called via
None (Instance) self Operates on instance data obj.method()
@classmethod cls Operates on class data, alternative constructor Class.method() or obj.method()
@staticmethod None Utility function, logically part of class Class.method() or obj.method()

1.4 Inheritance & MRO

  • Inheritance: Child class (subclass) derives attributes/methods from Parent class (superclass).

  • Multiple Inheritance: Class inherits from more than one base class.

  • Method Resolution Order (MRO): Sequence Python follows to search for a method in inheritance hierarchy. Uses C3 linearization algorithm.

    
    class A: pass
    
    class B(A): pass
    
    class C(A): pass
    
    class D(B, C): pass
    
    print(D.__mro__)  # (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
    
    

1.5 Polymorphism

  • Duck Typing: "If it walks like a duck and quacks like a duck, it is a duck." Object suitability determined by presence of methods/attributes, not its type.

    
    def make_sound(animal):
    
        animal.speak()  # Works for any object with .speak()
    
    
  • Method Overriding: Child class provides a specific implementation of a method already defined in its parent.

  • isinstance(obj, Class) vs type(obj) == Class: isinstance considers inheritance (True for subclass), type does exact match.

1.6 Encapsulation

  • Public: attribute (default). Accessible from anywhere.

  • Protected: _attribute (convention). Should be treated as non-public, accessible but intended for internal use.

  • Private: __attribute (name mangling). Python renames it to _ClassName__attribute, making it harder (not impossible) to access accidentally.

1.7 Special (Dunder) Methods

  • Enable operator overloading and integration with built-in functions.

  • __str__(self) -> str(obj), print(obj): Readable string for end-user.

  • __repr__(self) -> repr(obj): Unambiguous string, ideally eval(repr(obj)) recreates object.

  • __len__(self) -> len(obj)

  • __getitem__(self, key) -> obj[key] (for indexing)

  • __iter__(self) / __next__(self): Make object iterable.

1.8 Properties (@property)

  • Decorator to define a getter method that can be accessed like an attribute (without parentheses).

  • Enables managed (computed, validated) attributes.

    
    class Circle:
    
        def __init__(self, radius):
    
            self._radius = radius
    
        @property
    
        def radius(self):
    
            return self._radius
    
        @radius.setter
    
        def radius(self, value):
    
            if value <= 0: raise ValueError("Radius must be positive")
    
            self._radius = value
    
    c = Circle(5)
    
    print(c.radius)  # Calls getter
    
    c.radius = 10    # Calls setter
    
    

[!TIP] Exam Focus: Be prepared to write a class demonstrating inheritance, method overriding, and a @property. Know the difference between __str__ and __repr__.


2.0 Error and Exception Handling

2.1 Exception Hierarchy


BaseException

 โ”œโ”€โ”€ SystemExit

 โ”œโ”€โ”€ KeyboardInterrupt

 โ””โ”€โ”€ Exception  <-- Most user-defined/built-in errors inherit from here

      โ”œโ”€โ”€ StopIteration

      โ”œโ”€โ”€ ArithmeticError (ZeroDivisionError, OverflowError)

      โ”œโ”€โ”€ LookupError (IndexError, KeyError)

      โ”œโ”€โ”€ OSError (FileNotFoundError, PermissionError)

      โ”œโ”€โ”€ TypeError

      โ”œโ”€โ”€ ValueError

      โ””โ”€โ”€ ... 

2.2 try-except-else-finally Flow

  1. try: Block where exception might occur.

  2. except: Catches and handles specific exception(s). Executes if try block raises matching exception.

  3. else: Executes only if try block completes without raising an exception.

  4. finally: Always executes (whether exception occurred or not, even if return). Used for cleanup (e.g., closing files).

    
    try:
    
        result = 10 / 0
    
    except ZeroDivisionError as e:
    
        print(f"Caught: {e}")
    
    else:
    
        print("Division successful")  # Won't run
    
    finally:
    
        print("Cleanup complete")     # Always runs
    
    

2.3 Catching Multiple Exceptions


try:

    # risky code

except (TypeError, ValueError) as e:  # Tuple of exceptions

    print(f"Input error: {e}")

except Exception as e:                # Catch-all (use cautiously)

    print(f"Unexpected error: {e}")

2.4 & 2.5 Raising & Custom Exceptions

  • raise: Forces an exception to occur.

    
    if x < 0:
    
        raise ValueError("x must be non-negative")
    
    
  • Custom Exception: Inherit from Exception (or a more specific built-in).

    
    class InsufficientFundsError(Exception):
    
        def __init__(self, balance, amount):
    
            self.message = f"Balance {balance} too low for withdrawal {amount}"
    
            super().__init__(self.message)
    
    

2.6 Best Practices

  • Catch specific exceptions, not bare except: (which catches KeyboardInterrupt, SystemExit too).

  • Use exceptions for exceptional, unexpected conditions, not for regular control flow (e.g., don't use try-except to check if a key exists in a dict; use in operator).

  • Keep try block minimal (only the code that can raise the exception).

[!TIP] Common Pitfall: Using a bare except: clause. Always specify the exception type(s). Remember finally runs even if there's a return in try or except.


3.0 File I/O and Data Persistence

3.1 File Modes

Mode Meaning File Position Creates New?
'r' Read (default) Start No
'w' Write (truncate) Start Yes
'a' Append End Yes
'x' Exclusive creation Start Fails if exists
'b' Binary mode - -
't' Text mode (default) - -
'+' Update (read & write) Start Yes (with w/a/x)
  • Common Combinations: 'rb', 'w+', 'a+', 'r+'.

3.2 & 3.3 Reading & Writing Methods

  • Reading:

    • f.read(size=-1): Read entire file or size bytes.

    • f.readline(): Read single line (including \n).

    • f.readlines(): Read all lines into a list.

    • Iteration (Memory efficient): for line in f: ...

  • Writing:

    • f.write(str): Write a string.

    • f.writelines(list_of_strings): Write iterable of strings (no newlines added automatically).

3.4 The with Statement (Context Manager)

  • Ensures proper acquisition and release of resources (e.g., file closing), even if exceptions occur.

    
    with open('file.txt', 'r') as f:
    
        data = f.read()  # File automatically closed after block
    
    # f is closed here
    
    
  • Works with any object implementing __enter__() and __exit__().

3.5 Working with Data Formats

Module Primary Use Key Functions Notes
csv Comma/Tab-separated values reader(), writer(), DictReader, DictWriter Handles quoting, delimiters.
json JavaScript Object Notation (text) load()/loads() (parse), dump()/dumps() (serialize) Converts between Python objects (dict, list) and JSON strings.
pickle Python object serialization (binary) load()/loads(), dump()/dumps() Security Warning: Unpickling untrusted data can execute arbitrary code. Use only for trusted sources.

3.6 File/Directory Ops (os, pathlib)

  • os.path (legacy) / pathlib.Path (modern, OO): Path manipulation.

    
    from pathlib import Path
    
    p = Path("data/file.txt")
    
    p.exists(), p.is_file(), p.is_dir()
    
    p.mkdir(parents=True, exist_ok=True)  # Create dir
    
    for file in p.parent.iterdir(): ...    # List contents
    
    

[!TIP] Always use with open(...) for file operations. Prefer pathlib over os.path for new code. Never unpickle data from an untrusted source.


4.0 Modules and Packages

4.1 Module

  • A .py file containing Python definitions (functions, classes, variables) and statements.

  • Module name = filename (without .py).

4.2 import Variations


import module                # Access via module.func()

from module import func      # Access via func() directly

from module import *         # Imports all public names (discouraged)

import module as alias       # Access via alias.func()

from module import func as f # Access via f()

4.3 Module Search Path (sys.path)

Order: 1. Current directory, 2. PYTHONPATH env var, 3. Standard library dirs, 4. Site-packages.

4.4 if __name__ == "__main__":

  • Code inside this block runs only when the file is executed directly (python script.py), not when imported as a module.

  • Allows a file to be both importable (providing functions/classes) and executable (containing test/demo code).

4.5 Packages

  • Directory containing:

    • Special __init__.py file (can be empty, indicates package).

    • One or more module (.py) files or sub-packages.

    
    mypackage/
    
        __init__.py
    
        module1.py
    
        subpackage/
    
            __init__.py
    
            module2.py
    
    

4.6 Absolute vs. Relative Imports

  • Absolute: Full path from project root. from mypackage import module1

  • Relative: Using . for current package, .. for parent. from . import module1 or from ..subpackage import module2

  • Rule: Relative imports only within a package. Cannot be used in top-level script.

4.7 Standard Library Exploration

  • math: sqrt(), pi, ceil(), floor().

  • random: randint(), choice(), shuffle().

  • datetime: datetime.now(), date, timedelta.

  • collections: namedtuple, defaultdict, Counter, deque.

  • itertools: count(), cycle(), permutations(), combinations().

[!TIP] Know the purpose of __init__.py and the __name__ == "__main__" idiom. Understand when to use absolute vs relative imports.


5.0 Advanced Functions & Functional Concepts

5.1 Iterators & Iterables

  • Iterable: Object that can return an iterator (has __iter__() method). e.g., list, tuple, dict, string.

  • Iterator: Object with state (__next__() method) to produce next value on demand. Also has __iter__() returning itself.

    
    it = iter([1,2,3])
    
    print(next(it))  # 1
    
    print(next(it))  # 2
    
    

5.2 Generators

  • Generator Function: Uses yield instead of return. Returns a generator iterator (lazy, stateful).

    
    def count_up_to(n):
    
        i = 1
    
        while i <= n:
    
            yield i  # Pauses execution, returns value
    
            i += 1
    
    for num in count_up_to(5): ...  # 1,2,3,4,5
    
    
  • Generator Expression: (x**2 for x in range(10)) (like list comprehension but lazy).

  • Advantage: Memory efficient for large/streaming data.

5.3 Decorators

  • Function that takes another function as argument and returns a wrapper function to extend/modify its behavior.

    
    def my_decorator(func):
    
        def wrapper(*args, **kwargs):
    
            print("Before call")
    
            result = func(*args, **kwargs)
    
            print("After call")
    
            return result
    
        return wrapper
    
    @my_decorator
    
    def say_hello(): print("Hello")
    
    
  • Common Use Cases: Logging, timing, access control, caching, validation.

5.4 functools Module

Function Purpose
@wraps(func) Preserves metadata (__name__, __doc__) of original function in wrapper.
@lru_cache(maxsize=None) Memoization: Caches results of expensive function calls.
partial(func, *args, **kwargs) Freezes some arguments of a function, returning a new callable.

5.5 *args and **kwargs

  • *args: Packs positional arguments into a tuple.

  • **kwargs: Packs keyword arguments into a dict.

  • Unpacking: func(*list), func(**dict) passes elements as separate arguments.

    
    def func(a, b, c=0): ...
    
    args = (1, 2)
    
    kwargs = {'c': 3}
    
    func(*args, **kwargs)  # Equivalent to func(1, 2, c=3)
    
    

5.6 Lambda Functions & map/filter/sorted

  • Lambda: Anonymous, single-expression function. lambda x: x*2

  • map(func, iterable): Applies func to every item, returns iterator of results.

  • filter(func, iterable): Returns iterator of items where func(item) is True.

  • sorted(iterable, key=lambda x: x.attr): key function extracts a comparison key from each element.

[!TIP] Be comfortable writing a simple decorator and a generator function. Understand that map and filter return iterators (use list() to materialize). Prefer list comprehensions over map/filter for readability in simple cases.


6.0 Context Managers (Beyond with for Files)

6.1 Class-Based Context Manager

Implement __enter__() and __exit__(exc_type, exc_val, exc_tb).


class Timer:

    def __enter__(self):

        self.start = time.time()

        return self

    def __exit__(self, exc_type, exc_val, exc_tb):

        self.end = time.time()

        print(f"Elapsed: {self.end - self.start}")

    # If __exit__ returns True, exception is suppressed.

6.2 contextlib.contextmanager (Generator-Based)

  • Decorator for a generator function with a single yield.

  • Code before yield = __enter__, after yield = __exit__.

    
    from contextlib import contextmanager
    
    @contextmanager
    
    def managed_file(filename):
    
        f = open(filename, 'w')
    
        try:
    
            yield f
    
        finally:
    
            f.close()
    
    

6.3 Practical Applications

  • Acquiring/releasing locks (threading.Lock).

  • Opening/closing database connections.

  • Temporary changes to global state (e.g., sys.path, os.environ).

  • Temporary files/directories (tempfile module).


7.0 Best Practices, Style & Debugging

7.1 PEP 8 Quick Reference

  • Indentation: 4 spaces.

  • Line Length: Max 79 chars (code), 72 (comments/docstrings).

  • Naming:

    • snake_case for functions/variables.

    • CamelCase for classes.

    • UPPER_SNAKE_CASE for constants.

  • Imports: Standard library โ†’ third-party โ†’ local, each group separated by blank line.

  • Whitespace: Around operators, after commas, no extraneous spaces.

7.2 Code Readability

  • Use meaningful names (total_score vs ts).

  • Write docstrings for public modules, functions, classes, methods.

  • Keep functions short and single-purpose.

7.3 Debugging with pdb

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

  • Key Commands:

    | Command | Action | | :--- | :--- | | l (list) | Show current code location | | n (next) | Execute current line, step over function calls | | s (step) | Step into function calls | | c (continue) | Continue execution until next breakpoint | | p expr | Evaluate and print expression | | q (quit) | Exit debugger | | h (help) | Show help |

7.4 logging vs print()

  • logging: Configurable severity levels (DEBUG, INFO, WARNING, ERROR, CRITICAL). Can output to files, syslog, etc. with formatting. Production-ready.

    
    import logging
    
    logging.basicConfig(level=logging.INFO)
    
    logging.info("Process started")
    
    
  • print(): Simple, immediate stdout. Good for quick debugging, not for persistent logs.

7.5 Virtual Environments (venv)

  • Isolated Python environment with its own site-packages.

  • Create: python -m venv myenv

  • Activate:

    • Windows: myenv\Scripts\activate

    • Unix/macOS: source myenv/bin/activate

  • Deactivate: deactivate

  • Purpose: Manage project-specific dependencies without conflicts.

[!TIP] Always use a virtual environment for projects. Use logging for production diagnostics. Use pdb for interactive debugging. Follow PEP 8 for all submissions.

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