2.1 Advanced Object-Oriented Programming (OOP)
Class and Instance Namespaces
-
__dict__Attribute:-
Class Namespace:
ClassName.__dict__stores class attributes (methods, class variables). Shared across all instances. -
Instance Namespace:
instance.__dict__stores instance-specific attributes. Unique per object.
-
-
Attribute Lookup Order (MRO): When accessing
obj.attr, Python searches:-
Instance namespace (
obj.__dict__) -
Class namespace (
Class.__dict__) and its base classes (following MRO) -
__getattr__if defined
[!TIP] Use
ClassName.__mro__orClassName.mro()to view the Method Resolution Order. Crucial for understanding inheritance and super() calls. -
Properties and Descriptors
-
@propertyDecorator: Creates a data descriptor for managed attribute access.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 -
Custom Descriptors: Implement
__get__,__set__,__delete__.-
Data Descriptor: Implements
__set__. Takes precedence over instance dictionary. -
Non-data Descriptor: Only
__get__. Instance dictionary wins.
class Descriptor: def __get__(self, obj, objtype=None): ... def __set__(self, obj, value): ... -
__slots__ Mechanism
-
Purpose: Memory optimization (prevents
__dict__creation) and restricts allowed attributes. -
Syntax:
class MyClass: __slots__ = ('attr1', 'attr2') -
Implications:
-
Subclasses must also define
__slots__to add new slots. -
Pickling: Requires extra handling; default pickling may fail.
-
Inheritance: If a parent has
__slots__, child's__dict__is not created unless__slots__includes'__dict__'.
[!WARNING]
__slots__prevents adding arbitrary new attributes (obj.new_attr = xraisesAttributeError). -
Metaclasses
-
typeMetaclass: The default metaclass.type(name, bases, dict)creates classes. -
Custom Metaclass: Inherit from
typeand override__new__or__init__.class Meta(type): def __new__(cls, name, bases, dct): # Modify class before creation (e.g., add methods) return super().__new__(cls, name, bases, dct) class MyClass(metaclass=Meta): pass -
Use Cases:
-
API Enforcement: Ensure subclasses implement required methods.
-
Registration: Automatically register classes in a plugin system.
-
ORM Frameworks: Map class attributes to database columns.
-
Abstract Base Classes (ABCs)
-
abcModule:-
Inherit from
ABC(or usemetaclass=ABCMeta). -
Decorate methods with
@abstractmethod(or@abstractpropertyfor properties). -
Cannot instantiate a class with unimplemented abstract methods.
-
-
Virtual Subclasses:
ABC.register(SubClass)allowsissubclass(SubClass, ABC)to returnTruewithout actual inheritance (structural subtyping).[!TIP] ABCs define interfaces. Use them for
isinstance()checks and to enforce contracts in large codebases or frameworks.
2.2 Functional Programming Tools & Techniques
First-Class Functions & Closures
-
First-Class: Functions can be assigned to variables, passed as arguments, returned from other functions.
-
Closure Mechanics:
-
A nested function that captures variables from its enclosing scope.
-
Captured variables are stored in cell objects accessible via
func.__closure__. -
The
nonlocalkeyword allows modification of captured variables.
def outer(x): def inner(y): nonlocal x # Required to modify x x += y return x return inner -
Decorators (Deep Dive)
-
Function Decorator: A callable that takes a function and returns a replacement.
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(): ... -
Decorator with Arguments (Factory):
def repeat(times): def decorator(func): def wrapper(*args, **kwargs): for _ in range(times): result = func(*args, **kwargs) return result return wrapper return decorator @repeat(times=3) def greet(): ... -
Class Decorator: Applied to a class; typically modifies or wraps the class object.
-
functools.wraps: Decorator to copy metadata (__name__,__doc__) fromfunctowrapper. Always use it in custom decorators.[!TIP] Decorators are evaluated at import time, not runtime. Be mindful of side effects.
functools Module
-
partial: Freezes some arguments/keywords of a function, creating a new callable.from functools import partial int_base2 = partial(int, base=2) int_base2('1010') # Returns 10 -
lru_cache: Memoization with Least Recently Used eviction.from functools import lru_cache @lru_cache(maxsize=128, typed=False) def fib(n): if n < 2: return n return fib(n-1) + fib(n-2)typed=Truetreats3and3.0as distinct calls.
-
reduce: Applies a binary function cumulatively to items of an iterable.from functools import reduce reduce(lambda x, y: x*y, [1,2,3,4]) # (((1*2)*3)*4) = 24
Iterators and Generators (Advanced)
-
Iterator Protocol: Objects with
__iter__()(returns iterator) and__next__()(returns next item or raisesStopIteration). -
Generator Functions: Use
yieldto produce a sequence lazily. State is saved between calls.yield from: Delegates to a subgenerator. Used for coroutines and complex generator chains.
-
Generator Expressions:
(x**2 for x in range(10)). Memory-efficient vs. list comprehensions[x**2 for x in range(10)]. -
Context Managers for Generators:
@contextlib.contextmanagerdecorator allows writing a generator that yields once, with setup beforeyieldand teardown after.from contextlib import contextmanager @contextmanager def managed_file(filename): f = open(filename, 'w') try: yield f finally: f.close()
2.3 Concurrency and Parallelism
Threading (threading module)
-
ThreadClass:t = Thread(target=func, args=(...));t.start(),t.join(). -
Synchronization Primitives:
-
Lock: Basic mutual exclusion. -
RLock: Reentrant lock (same thread can acquire multiple times). -
Semaphore: Counter-based access for limited resources. -
Event: Thread signaling (set/clear/wait). -
Condition: Complex coordination (wait/notify).
-
-
Daemon Threads:
t.daemon = True(orThread(..., daemon=True)). Dies when main program exits. -
Thread-Local Data:
threading.local()creates an object whose attributes are local to the current thread. -
GIL (Global Interpreter Lock):
-
Only one thread executes Python bytecode at a time.
-
I/O-bound tasks: Threading effective (GIL released during I/O).
-
CPU-bound tasks: Threading ineffective; use
multiprocessing.
-
Multiprocessing (multiprocessing module)
-
ProcessClass: Similar API toThread. Spawns separate Python interpreter. -
IPC (Inter-Process Communication):
-
Queue: Process-safe FIFO queue. -
Pipe: Two-way communication between two processes.
-
-
Process Synchronization:
Lock,Semaphore(for shared memory coordination). -
PoolClasses:-
Pool: Process pool for parallel execution (map,apply_async). -
ThreadPool: (Inmultiprocessing.pool) Hybrid approach.
-
-
Sharing State:
-
Value,Array: Shared memory primitives (ctypes). -
Manager: Provides server process managing shared objects (dict, list, Namespace).
-
Asynchronous Programming (asyncio)
-
Core Concepts:
-
Event Loop: Central scheduler.
-
Coroutine: Defined with
async def. Must be awaited. -
await: Pauses coroutine, yields control to event loop.
-
-
Tasks:
asyncio.create_task(coro)schedules coroutine.asyncio.gather(*tasks)waits for all.asyncio.wait(tasks)returns completed/pending. -
Async Context Managers/Iterators:
async with,async forrequire__aenter__,__aiter__, etc. -
Running Async Code:
asyncio.run(main()) # Top-level entry (Python 3.7+) loop = asyncio.get_event_loop() loop.run_until_complete(main()) -
Async I/O:
asyncio.open_connection()(streams),asyncio.create_subprocess_exec(). -
Async Synchronization:
asyncio.Lock,asyncio.Event,asyncio.Semaphore(non-blocking, awaitable). -
asyncio.to_thread(func, *args): Runs blockingfuncin a thread, returns awaitable.
[!COMPARISON] Concurrency Model Choice:
| Model | Best For | GIL Impact |
|-------|----------|------------|
|
threading| I/O-bound (network, file) | Released during I/O |
|
multiprocessing| CPU-bound (computation) | Bypassed (separate processes) |
|
asyncio| High-concurrency I/O (thousands of connections) | Single-threaded, cooperative |
2.4 Performance Analysis and Optimization
Profiling Code
-
timeModule: Simple wall-clock timing (time.perf_counter()). -
cProfile: Deterministic profiler. Generates call statistics.python -m cProfile -s cumulative my_script.pyUse
pstatsmodule to sort/filter output. -
line_profiler(Third-party):@profiledecorator,kernprof -l -v script.py. Line-by-line timing. -
memory_profiler(Third-party):@profiledecorator,python -m memory_profiler script.py. Tracks line-wise memory usage.
Optimization Strategies
-
Algorithmic Complexity (Big O): Primary optimization target. Reduce time complexity (e.g., O(n²) → O(n log n)).
-
Leverage Built-ins & C Libraries: Built-in functions (
sum,map) and libraries (collections,itertools) are implemented in C → faster. -
Local Variable Lookups: Accessing local variables (
x) is faster than globals (global_x) or attributes (obj.x). -
Data Structures:
-
list: Dynamic array, fast indexed access O(1), slow insert/delete O(n). -
deque(collections): Double-ended queue, fast appends/pops at both ends O(1). -
set: Hash table, fast membership test O(1), unordered.
-
-
String Concatenation: Avoid
s += "a"in loops (creates new string each time). Use"".join(list_of_strings). -
JIT Compilation:
numbadecorator (@njit) compiles Python to machine code (numeric code). -
C Extensions/Cython: Write performance-critical sections in C or Cython (Python-like syntax compiled to C).
[!CRITICAL] Optimization Rule: Profile before optimizing. Focus on bottlenecks (often 20% of code causing 80% of slowdown). Algorithmic change > micro-optimization.
2.5 Packaging and Distribution
Project Structure
-
Modern Standard:
pyproject.toml(PEP 518, 621). Declares build system & metadata. -
Legacy:
setup.py(executable script) +setup.cfg(declarative config). -
Layout:
-
Flat: Package dir at root.
-
src/: Package insidesrc/directory (prevents import confusion).
-
setuptools & wheel
-
Metadata (
setup.cfg/pyproject.toml):[project] name = "mypackage" version = "0.1.0" dependencies = ["requests>=2.0"] [tool.setuptools.packages.find] where = ["src"] -
Distributions:
-
sdist(source):.tar.gzarchive. Platform-independent. -
bdist_wheel(binary):.whlfile. Pre-compiled, faster install.
-
-
Development Mode:
pip install -e .→ installs as symlink; changes reflect immediately.
pip and Dependencies
-
requirements.txt(legacy):package==version(pinned). Generated bypip freeze. -
pyproject.toml(modern):[project] dependencies = ["package>=1.0"]. -
Version Specifiers:
-
==: Exact version. -
>=: Minimum version. -
~=: Compatible release (PEP 440).~=1.4→>=1.4, ==1.*.
-
-
Virtual Environments: Isolate dependencies (
python -m venv .venv,conda create).
Creating and Publishing Packages
-
Build:
python -m build(createsdist/with.tar.gzand.whl). -
Test Upload:
twine upload --repository testpypi dist/*. -
Production Upload:
twine upload dist/*(to PyPI). -
Versioning: Semantic Versioning (
MAJOR.MINOR.PATCH). Pre-releases:1.0.0a1,1.0.0rc1.
2.6 Advanced Modules and Ecosystem
collections Module
| Class | Purpose | Key Feature |
|---|---|---|
namedtuple |
Tuple with named fields | Point = namedtuple('Point', 'x y') |
deque |
Double-ended queue | appendleft(), popleft() O(1) |
Counter |
Multiset/count hashables | Counter('abracadabra') → {'a':5, 'b':2, ...} |
OrderedDict |
Insertion-ordered dict | Less needed in Python 3.7+ (dict is ordered) |
defaultdict |
Dict with default factory | dd = defaultdict(list); dd['key'].append(1) |
ChainMap |
Combine multiple mappings | Search through dicts sequentially |
itertools Module
-
Infinite:
count(start=0, step=1),cycle(iterable),repeat(elem, n=None). -
Finite:
-
chain(*iterables): Concatenate. -
compress(data, selectors): Filter by boolean mask. -
dropwhile(pred, seq),takewhile(pred, seq). -
islice(iterable, start, stop, step).
-
-
Combinatoric:
-
product(*iterables, repeat=1): Cartesian product. -
permutations(iterable, r=None): r-length permutations, no repeats. -
combinations(iterable, r): r-length combinations, no order, no repeats. -
combinations_with_replacement(iterable, r).
-
contextlib Utilities
-
@contextmanager: Decorator for generator-based context managers. -
closing(obj): Context manager callingobj.close()on exit. -
suppress(*exceptions): Ignores specified exceptions in block. -
redirect_stdout/redirect_stderr(new_target): Redirects output streams.
dataclasses Module (Python 3.7+)
from dataclasses import dataclass, field
@dataclass(order=True, frozen=False)
class Employee:
name: str
id: int = field(default=0, compare=False)
skills: list[str] = field(default_factory=list)
def __post_init__(self):
# Custom validation/modification
self.name = self.name.title()
-
Auto-generates
__init__,__repr__,__eq__. -
field(): Customize individual fields (default,default_factory,compare,metadata). -
__post_init__(): Hook for additional initialization. -
frozen=True: Makes instances immutable (likenamedtuple).
Type Hints & typing Module (Advanced)
-
Generics:
from typing import TypeVar, Generic, List T = TypeVar('T') class Box(Generic[T]): def __init__(self, item: T): ... def get(self) -> T: ... -
Protocols (Structural Subtyping):
from typing import Protocol class SupportsClose(Protocol): def close(self) -> None: ... def close_all(items: List[SupportsClose]): ... -
TypedDict: Dict with specific key types.from typing import TypedDict class User(TypedDict): name: str age: int -
Annotated: Attach metadata to type hints. -
@overload: Multiple type signatures for a function. -
mypy: Static type checker (mypy script.py).
2.7 Testing and Quality Assurance
unittest Framework
-
Structure:
import unittest class TestMath(unittest.TestCase): def setUp(self): ... # Runs before each test def tearDown(self): ... # Runs after each test def test_add(self): self.assertEqual(1+1, 2) with self.assertRaises(TypeError): "a" + 1 -
Test Discovery:
python -m unittest discover -s tests/. -
Assertions:
assertEqual(a,b),assertTrue(x),assertRaises(exc, func, *args).
pytest Framework (Industry Standard)
-
Simple Tests: Just functions with
assert.def test_add(): assert 1+1 == 2 -
Fixtures:
import pytest @pytest.fixture(scope="module") def db_connection(): conn = connect_db() yield conn conn.close() def test_query(db_connection): db_connection.execute("SELECT ...") -
Parametrize:
@pytest.mark.parametrize("a,b,expected", [(1,1,2), (2,3,5)]) def test_add(a,b,expected): assert a+b == expected -
Markers:
@pytest.mark.skip(reason="..."),@pytest.mark.xfail. -
conftest.py: Shared fixtures across test files. -
Capturing Output:
capsys(stdout/stderr),capfd(file descriptors).
Test Doubles (unittest.mock)
-
Mock/MagicMock: Replace objects, record calls.from unittest.mock import Mock, patch m = Mock() m.method(1,2,3) m.method.assert_called_once_with(1,2,3) -
patch: Temporarily replace an object.@patch('module.ClassName') def test_something(mock_class): mock_class.return_value.method.return_value = 42 ... -
Stub: Predefined response.
-
Spy: Wraps real object, records calls.
Code Coverage
-
coverage.py:coverage run -m pytest coverage report -m # Terminal report coverage html # Generates htmlcov/ -
Measures line coverage and branch coverage.
-
Set target in
.coveragerc:[run] branch = True.
2.8 Debugging and Development Tools
pdb - The Python Debugger
-
Basic Commands:
-
l(list): Show current code. -
n(next): Execute current line, step over. -
s(step): Step into function. -
c(continue): Resume execution. -
p expr: Print expression value. -
q(quit): Exit debugger.
-
-
Breakpoints:
-
In code:
breakpoint()(Python 3.7+ → callssys.breakpointhook, defaultpdb.set_trace()). -
In pdb:
b filename:linenoorb function.
-
-
Post-Mortem Debugging:
import pdb; pdb.pm()after exception.
Logging (logging module)
-
Levels:
DEBUG<INFO<WARNING<ERROR<CRITICAL. -
Basic Config:
import logging logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') -
Hierarchy:
Logger→Handler→Formatter.logger = logging.getLogger(__name__) handler = logging.FileHandler('app.log') formatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s') handler.setFormatter(formatter) logger.addHandler(handler) -
Dict Config:
logging.config.dictConfig({...})for complex setups.
rich Library (Modern Terminal Formatting)
-
Pretty Print:
from rich import print; print({"data": [1,2,3]})→ syntax-highlighted, aligned. -
Console Objects:
console = Console();console.print("[bold red]Error![/]"). -
Panels/Tables:
Panel("text", title="Log"),Table()for structured data. -
Traceback:
from rich.traceback import install; install()→ colorful tracebacks.
IPython / Jupyter Enhancements
-
Magic Commands:
-
%timeit expr: Measure execution time of single statement. -
%run script.py: Run external script in IPython namespace. -
%debug: Launch debugger at last exception. -
%load_ext module: Load extension.
-
-
Shell Interaction:
-
!ls -la: Run shell command. -
obj?/obj??: Show docstring / source code.
-
-
%debugAfter Exception: Automatically enters pdb at point of failure.
[!ADVANCED] Combine tools: Use
pdb/%debugfor interactive debugging,loggingfor persistent logs, andrichfor enhanced console output in development scripts.