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
classkeyword. -
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)vstype(obj) == Class:isinstanceconsiders inheritance (Truefor subclass),typedoes 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, ideallyeval(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
-
try: Block where exception might occur. -
except: Catches and handles specific exception(s). Executes iftryblock raises matching exception. -
else: Executes only iftryblock completes without raising an exception. -
finally: Always executes (whether exception occurred or not, even ifreturn). 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 catchesKeyboardInterrupt,SystemExittoo). -
Use exceptions for exceptional, unexpected conditions, not for regular control flow (e.g., don't use
try-exceptto check if a key exists in a dict; useinoperator). -
Keep
tryblock minimal (only the code that can raise the exception).
[!TIP] Common Pitfall: Using a bare
except:clause. Always specify the exception type(s). Rememberfinallyruns even if there's areturnintryorexcept.
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 orsizebytes. -
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. Preferpathliboveros.pathfor new code. Never unpickle data from an untrusted source.
4.0 Modules and Packages
4.1 Module
-
A
.pyfile 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__.pyfile (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 module1orfrom ..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__.pyand 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
yieldinstead ofreturn. 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): Appliesfuncto every item, returns iterator of results. -
filter(func, iterable): Returns iterator of items wherefunc(item)isTrue. -
sorted(iterable, key=lambda x: x.attr):keyfunction extracts a comparison key from each element.
[!TIP] Be comfortable writing a simple decorator and a generator function. Understand that
mapandfilterreturn iterators (uselist()to materialize). Prefer list comprehensions overmap/filterfor 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__, afteryield=__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 (
tempfilemodule).
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_casefor functions/variables. -
CamelCasefor classes. -
UPPER_SNAKE_CASEfor 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_scorevsts). -
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
loggingfor production diagnostics. Usepdbfor interactive debugging. Follow PEP 8 for all submissions.