๐Ÿ–ฅ๏ธ VALLEYS & BYTES โ€ข PYTHON OOP โ€ข 2026 Edition

classes โ€ข inheritance โ€ข dunder โ€ข SOLID โ€ข design patterns โ€ข modern Python 3.12+
Advertisement

๐Ÿ“ฆ Classes & Instances fundamentals

__init__ โ€ข self โ€ข class vs instance attributes
class Employee: company = "TechCorp" # class variable - shared def __init__(self, name, salary): self.name = name # instance variable - unique self.salary = salary def give_raise(self, percent): self.salary *= (1 + percent/100) def __repr__(self): return f"Employee({self.name}, ${self.salary:,.0f})" alice = Employee("Alice Chen", 95000) bob = Employee("Bob Smith", 82000) alice.give_raise(10) print(alice) # Employee(Alice Chen, $104,500)
#python__init__self__dict__
Advertisement

๐Ÿงฌ Inheritance & MRO is-a relationships

super() โ€ข method overriding โ€ข C3 linearization
class Vehicle: def __init__(self, brand): self.brand = brand def move(self): return f"{self.brand} moves" class Car(Vehicle): def __init__(self, brand, model): super().__init__(brand) # call parent constructor self.model = model def move(self): # override return f"{self.brand} {self.model} drives" class Boat(Vehicle): def move(self): return f"{self.brand} sails" vehicles = [Car("Tesla", "Model 3"), Boat("Yamaha")] for v in vehicles: print(v.move())
super()MROpolymorphism

๐Ÿ”’ Encapsulation name mangling

protected _single โ€ข private __double โ€ข properties
class BankAccount: def __init__(self, owner, balance): self.owner = owner self.__balance = balance # name mangled to _BankAccount__balance @property def balance(self): # getter return self.__balance @balance.setter def balance(self, amount): # setter with validation if amount < 0: raise ValueError("Balance cannot be negative") self.__balance = amount def withdraw(self, amount): if amount <= self.__balance: self.__balance -= amount return amount raise ValueError("Insufficient funds") acc = BankAccount("Bob", 5000) print(acc.balance) # 5000 (uses getter) acc.balance = 6000 # uses setter
@property@setterencapsulation

๐ŸŽญ Polymorphism duck typing + ABC

abstract base classes โ€ข protocols โ€ข duck typing
from abc import ABC, abstractmethod class Drawable(ABC): @abstractmethod def draw(self): pass @abstractmethod def area(self): pass class Circle(Drawable): def __init__(self, radius): self.radius = radius def draw(self): return "โ—‹" def area(self): return 3.14159 * self.radius ** 2 class Square(Drawable): def __init__(self, side): self.side = side def draw(self): return "โ–ก" def area(self): return self.side ** 2 # Duck typing example - any object with .draw() works def render(shape): print(shape.draw()) # doesn't care about type render(Circle(5)) # โ—‹ render(Square(4)) # โ–ก
ABCduck typingprotocol
Advertisement

โš™๏ธ Dunder Methods operator overloading

__repr__ โ€ข __len__ โ€ข __eq__ โ€ข __call__
class Book: def __init__(self, title, author, pages): self.title = title self.author = author self.pages = pages def __repr__(self): # unambiguous representation return f"Book('{self.title}', '{self.author}')" def __str__(self): # user-friendly string return f"'{self.title}' by {self.author}" def __len__(self): # enable len() return self.pages def __eq__(self, other): # enable == return self.title == other.title and self.author == other.author def __lt__(self, other): # enable sorting return self.pages < other.pages def __call__(self): # object as function return f"Reading {self.title}" book = Book("1984", "Orwell", 328) print(book()) # Reading 1984 (__call__) print(len(book)) # 328 (__len__)
__repr____eq____call__

๐Ÿท๏ธ Class & Static Methods

@classmethod โ€ข @staticmethod โ€ข factories
from datetime import datetime class Person: def __init__(self, name, age): self.name = name self.age = age @classmethod def from_birth_year(cls, name, birth_year): """Alternative constructor - creates Person from birth year""" age = datetime.now().year - birth_year return cls(name, age) @classmethod def from_string(cls, data_string): """Factory: 'name,age' -> Person""" name, age = data_string.split(',') return cls(name.strip(), int(age)) @staticmethod def is_adult(age): """Utility function - no self or cls needed""" return age >= 18 def __repr__(self): return f"Person('{self.name}', {self.age})" p1 = Person.from_birth_year("Alice", 1995) p2 = Person.from_string("Bob, 25") print(Person.is_adult(20)) # True
@classmethod@staticmethodfactory

๐Ÿ”— Composition vs Inheritance

"has-a" โ€ข "uses-a" โ€ข loose coupling
class Engine: def start(self): return "Engine started" def stop(self): return "Engine stopped" class Wheels: def __init__(self, count=4): self.count = count def rotate(self): return f"{self.count} wheels rotating" class Car: """Composition: Car has an Engine and Wheels""" def __init__(self): self.engine = Engine() # composition - strong ownership self.wheels = Wheels() # parts created with car def drive(self): return f"{self.engine.start()}, {self.wheels.rotate()}" class MusicPlayer: def play(self): return "Playing music" class ModernCar(Car): """Inheritance: ModernCar is a Car with extra features""" def __init__(self): super().__init__() self.player = MusicPlayer() # composition within inheritance def drive_with_music(self): return f"{self.drive()} while {self.player.play()}" my_car = ModernCar() print(my_car.drive_with_music())
has-acompositionloose coupling

๐Ÿงฐ Dataclasses (3.7+) modern Python

@dataclass โ€ข frozen โ€ข field
from dataclasses import dataclass, field from typing import List @dataclass(order=True, frozen=False) class InventoryItem: name: str unit_price: float quantity: int = 0 @property def total_cost(self): return self.unit_price * self.quantity @dataclass(frozen=True) # immutable class Point: x: float y: float z: float = 0.0 def distance_from_origin(self): return (self.x**2 + self.y**2 + self.z**2) ** 0.5 @dataclass class ShoppingCart: items: List[InventoryItem] = field(default_factory=list) customer: str = "Guest" def add_item(self, item): self.items.append(item) @property def total(self): return sum(item.total_cost for item in self.items) item = InventoryItem("Laptop", 999.99, 2) print(item.total_cost) # 1999.98
@dataclassfrozentype hints
Advertisement

๐Ÿง  Advanced OOP Concepts

๐Ÿ” Multiple Inheritance C3 linearization

MRO โ€ข diamond problem โ€ข mixins
class LoggerMixin: """Mixin adding logging capabilities""" def log(self, message): print(f"[{self.__class__.__name__}] {message}") class SerializableMixin: """Mixin for JSON serialization""" def to_dict(self): return self.__dict__ def to_json(self): import json return json.dumps(self.to_dict()) class ValidatorMixin: """Mixin for data validation""" def validate_positive(self, value, name): if value < 0: raise ValueError(f"{name} must be positive") return value class Product(LoggerMixin, SerializableMixin, ValidatorMixin): def __init__(self, name, price): self.name = name self.price = self.validate_positive(price, "price") self.log(f"Created product: {name}") def __repr__(self): return f"Product({self.name}, ${self.price})" # Method Resolution Order demonstration class A: pass class B(A): pass class C(A): pass class D(B, C): pass # MRO: D -> B -> C -> A -> object print(D.__mro__) # Shows resolution order
mixinMROdiamond

๐ŸŽจ Descriptors __get__ / __set__

Power behind @property, @classmethod, @staticmethod
class PositiveNumber: """Descriptor that only allows positive values""" def __set_name__(self, owner, name): self.name = name def __get__(self, obj, objtype=None): if obj is None: return self return obj.__dict__.get(self.name, 0) def __set__(self, obj, value): if value < 0: raise ValueError(f"{self.name} must be >= 0") obj.__dict__[self.name] = value def __delete__(self, obj): del obj.__dict__[self.name] class Temperature: celsius = PositiveNumber() fahrenheit = PositiveNumber() def __init__(self, celsius=0, fahrenheit=32): self.celsius = celsius self.fahrenheit = fahrenheit @property def kelvin(self): return self.celsius + 273.15 temp = Temperature(25, 77) print(temp.celsius) # 25 # temp.celsius = -10 # ValueError: celsius must be >= 0
__get____set__descriptor

๐Ÿ—ƒ๏ธ __slots__ memory optimization

Replace __dict__ with fixed array โ€“ 40-60% less memory
class PointSlots: """Memory-efficient point class""" __slots__ = ('x', 'y') # No __dict__ created def __init__(self, x, y): self.x = x self.y = y def distance(self, other): return ((self.x - other.x)**2 + (self.y - other.y)**2) ** 0.5 class PointDict: """Traditional point with __dict__ overhead""" def __init__(self, x, y): self.x = x self.y = y # Memory comparison import sys ps = PointSlots(10, 20) pd = PointDict(10, 20) print(f"__slots__ size: {sys.getsizeof(ps)} bytes") print(f"__dict__ size: {sys.getsizeof(pd)} bytes") # ps.z = 30 # AttributeError - can't add new attributes # For data-heavy applications (millions of objects) class Vector3D: __slots__ = ('x', 'y', 'z', '_length') def __init__(self, x, y, z): self.x, self.y, self.z = x, y, z self._length = None @property def length(self): if self._length is None: self._length = (self.x**2 + self.y**2 + self.z**2) ** 0.5 return self._length
__slots__memoryperformance

๐Ÿ”„ __iter__ & __next__ custom iterators

Make any object iterable โ€ข generators โ€ข lazy evaluation
class Fibonacci: """Iterator that generates Fibonacci numbers""" def __init__(self, max_count): self.max_count = max_count self.count = 0 self.a, self.b = 0, 1 def __iter__(self): return self # iterator returns itself def __next__(self): if self.count >= self.max_count: raise StopIteration self.count += 1 self.a, self.b = self.b, self.a + self.b return self.a def __repr__(self): return f"Fibonacci({self.max_count})" # Using the iterator fib = Fibonacci(10) for num in fib: print(num, end=' ') # 1 1 2 3 5 8 13 21 34 55 # Generator version (simpler) def fibonacci_gen(max_count): a, b = 0, 1 for _ in range(max_count): a, b = b, a + b yield a # Reversible iterable class Range: def __init__(self, start, end): self.start = start self.end = end def __iter__(self): return (i for i in range(self.start, self.end)) def __reversed__(self): return (i for i in range(self.end-1, self.start-1, -1))
__iter____next__generator
Advertisement

๐Ÿงต Metaclasses class factories

type โ€ข __new__ โ€ข class creation control
class SingletonMeta(type): """Metaclass that creates singleton classes""" _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super().__call__(*args, **kwargs) return cls._instances[cls] class AutoPropertyMeta(type): """Metaclass that auto-adds properties""" def __new__(mcs, name, bases, namespace): # Auto-create properties for attributes starting with '_' for key, value in list(namespace.items()): if key.startswith('_') and not key.startswith('__'): prop_name = key[1:] # remove leading underscore namespace[prop_name] = property( lambda self, k=key: getattr(self, k), lambda self, val, k=key: setattr(self, k, val) ) return super().__new__(mcs, name, bases, namespace) class Logger(metaclass=SingletonMeta): """Only one instance ever created""" def __init__(self): self.logs = [] def log(self, msg): self.logs.append(msg) print(f"LOG: {msg}") class Person(metaclass=AutoPropertyMeta): def __init__(self, name): self._name = name # automatically gets .name property p = Person("Alice") print(p.name) # Alice (auto-generated property)
metaclasstypesingleton

๐Ÿช __getattr__ & __getattribute__

Dynamic attribute handling โ€ข proxies โ€ข fallbacks
class DynamicAttributes: """Returns default for missing attributes""" def __init__(self): self._data = {} def __getattr__(self, name): """Called only when normal lookup fails""" if name.startswith('get_'): key = name[4:] # remove 'get_' prefix return lambda: self._data.get(key, None) return f"Attribute '{name}' not found" def __setattr__(self, name, value): """Intercepts all attribute setting""" if name.startswith('_'): super().__setattr__(name, value) # bypass for internal else: self._data[name] = value def __getattribute__(self, name): """Called for EVERY attribute access - use carefully!""" print(f"Accessing: {name}") return super().__getattribute__(name) class Proxy: """Proxy pattern with __getattr__""" def __init__(self, target): self._target = target def __getattr__(self, name): """Delegate to target object""" print(f"Proxy: delegating {name}") return getattr(self._target, name) def __setattr__(self, name, value): if name == '_target': super().__setattr__(name, value) else: print(f"Proxy: setting {name} on target") setattr(self._target, name, value) obj = DynamicAttributes() obj.name = "test" print(obj.name) # test (via __setattr__ and __getattribute__)
__getattr____setattr__proxy

โšก __new__ vs __init__ creation pipeline

__new__ creates โ€ข __init__ initializes
class ImmutablePoint: """Immutable point using __new__""" def __new__(cls, x, y): # Create instance instance = super().__new__(cls) # Initialize once and make immutable instance._x = x instance._y = y return instance def __init__(self, x, y): # This won't be called if __new__ doesn't call it # But if it is, we need to handle it pass @property def x(self): return self._x @property def y(self): return self._y def __setattr__(self, name, value): if hasattr(self, '_x'): # Already initialized raise AttributeError("ImmutablePoint is immutable") super().__setattr__(name, value) class DatabaseConnection: """Connection pool with __new__""" _pool = {} def __new__(cls, db_name): if db_name not in cls._pool: instance = super().__new__(cls) cls._pool[db_name] = instance return instance return cls._pool[db_name] def __init__(self, db_name): self.db_name = db_name # This runs every time, even if returning cached # Usage p1 = ImmutablePoint(3, 4) p2 = ImmutablePoint(3, 4) print(p1.x, p1.y) # 3 4 # p1.x = 5 # AttributeError
__new____init__immutable

๐Ÿ”‘ __init_subclass__ subclass hooks

Simpler alternative to metaclasses for subclass registration
class PluginBase: """Base class that auto-registers all plugins""" _registry = {} def __init_subclass__(cls, name=None, **kwargs): """Called when a subclass is created""" super().__init_subclass__(**kwargs) plugin_name = name or cls.__name__ cls.name = plugin_name PluginBase._registry[plugin_name] = cls print(f"Registered plugin: {plugin_name}") def process(self, data): raise NotImplementedError class JSONPlugin(PluginBase, name="json"): def process(self, data): import json return json.dumps(data) class CSVPlugin(PluginBase, name="csv"): def process(self, data): return ",".join(str(x) for x in data) class XMLPlugin(PluginBase): def process(self, data): return f"{data}" # All plugins auto-registered print(PluginBase._registry) # {'json': JSONPlugin, 'csv': CSVPlugin, 'XMLPlugin': XMLPlugin} # Plugin factory def process_data(data, format_name): plugin_cls = PluginBase._registry.get(format_name) if not plugin_cls: raise ValueError(f"Unknown format: {format_name}") return plugin_cls().process(data)
__init_subclass__registryplugin
Advertisement

๐Ÿ—๏ธ Design Patterns with Python

๐Ÿญ Factory Pattern creational

Abstract factory โ€ข factory method โ€ข dependency injection
from abc import ABC, abstractmethod class Button(ABC): @abstractmethod def render(self): pass @abstractmethod def on_click(self): pass class WindowsButton(Button): def render(self): return "Windows style button" def on_click(self): return "Windows click sound" class MacButton(Button): def render(self): return "Mac style button" def on_click(self): return "Mac click sound" class Checkbox(ABC): @abstractmethod def render(self): pass class WindowsCheckbox(Checkbox): def render(self): return "Windows style checkbox" class MacCheckbox(Checkbox): def render(self): return "Mac style checkbox" class GUIFactory(ABC): @abstractmethod def create_button(self) -> Button: pass @abstractmethod def create_checkbox(self) -> Checkbox: pass class WindowsFactory(GUIFactory): def create_button(self): return WindowsButton() def create_checkbox(self): return WindowsCheckbox() class MacFactory(GUIFactory): def create_button(self): return MacButton() def create_checkbox(self): return MacCheckbox() # Factory function (simpler approach) def get_factory(os_type): factories = { 'windows': WindowsFactory, 'mac': MacFactory } return factories.get(os_type, WindowsFactory)() # Usage factory = get_factory('mac') button = factory.create_button() checkbox = factory.create_checkbox() print(button.render()) # Mac style button
factorycreationalDI

๐Ÿ“ฆ Builder Pattern complex construction

Step-by-step โ€ข fluent interface โ€ข immutable result
class Computer: def __init__(self): self.cpu = None self.ram = None self.storage = None self.gpu = None self.os = None def __repr__(self): parts = [] if self.cpu: parts.append(f"CPU:{self.cpu}") if self.ram: parts.append(f"RAM:{self.ram}GB") if self.storage: parts.append(f"Storage:{self.storage}") if self.gpu: parts.append(f"GPU:{self.gpu}") if self.os: parts.append(f"OS:{self.os}") return "Computer(" + ", ".join(parts) + ")" class ComputerBuilder: def __init__(self): self.computer = Computer() def add_cpu(self, cpu): self.computer.cpu = cpu return self def add_ram(self, ram_gb): self.computer.ram = ram_gb return self def add_storage(self, storage): self.computer.storage = storage return self def add_gpu(self, gpu): self.computer.gpu = gpu return self def install_os(self, os): self.computer.os = os return self def build(self): return self.computer # Usage with method chaining gaming_pc = (ComputerBuilder() .add_cpu("Intel i9-13900K") .add_ram(64) .add_storage("2TB NVMe SSD") .add_gpu("NVIDIA RTX 4090") .install_os("Windows 11") .build()) print(gaming_pc) # Computer(CPU:Intel i9-13900K, RAM:64GB, Storage:2TB NVMe SSD, GPU:NVIDIA RTX 4090, OS:Windows 11) workstation = (ComputerBuilder() .add_cpu("AMD Threadripper") .add_ram(128) .add_storage("4TB NVMe + 8TB HDD") .install_os("Ubuntu 22.04") .build())
fluentbuilderchaining

๐ŸŽฏ Strategy Pattern behavioral

Encapsulate algorithms โ€ข runtime selection โ€ข dependency injection
from typing import List, Callable import math # Strategy as interface class SortingStrategy: def sort(self, data: List[int]) -> List[int]: raise NotImplementedError class BubbleSort(SortingStrategy): def sort(self, data): arr = data[:] n = len(arr) for i in range(n): for j in range(0, n-i-1): if arr[j] > arr[j+1]: arr[j], arr[j+1] = arr[j+1], arr[j] return arr class QuickSort(SortingStrategy): def sort(self, data): if len(data) <= 1: return data pivot = data[len(data)//2] left = [x for x in data if x < pivot] middle = [x for x in data if x == pivot] right = [x for x in data if x > pivot] return self.sort(left) + middle + self.sort(right) class PythonSort(SortingStrategy): def sort(self, data): return sorted(data) # Strategy as function (Pythonic approach) def bubble_sort(data): arr = data[:] n = len(arr) for i in range(n): for j in range(0, n-i-1): if arr[j] > arr[j+1]: arr[j], arr[j+1] = arr[j+1], arr[j] return arr class Sorter: def __init__(self, strategy: SortingStrategy): self._strategy = strategy def set_strategy(self, strategy: SortingStrategy): self._strategy = strategy def sort(self, data): return self._strategy.sort(data) def benchmark(self, data): import time start = time.time() result = self.sort(data) elapsed = time.time() - start return result, elapsed data = [64, 34, 25, 12, 22, 11, 90] sorter = Sorter(PythonSort()) print(sorter.sort(data)) sorter.set_strategy(BubbleSort()) result, time_taken = sorter.benchmark(data) print(f"Bubble sort took {time_taken:.6f}s")
strategyDIopen/closed

๐Ÿ‘€ Observer Pattern pub-sub

Event handling โ€ข loose coupling โ€ข notifications
from abc import ABC, abstractmethod from typing import List, Any class Observer(ABC): @abstractmethod def update(self, subject: 'Subject', event: str, data: Any): pass class Subject: def __init__(self): self._observers: List[Observer] = [] self._state = {} def attach(self, observer: Observer): if observer not in self._observers: self._observers.append(observer) def detach(self, observer: Observer): self._observers.remove(observer) def notify(self, event: str, data: Any = None): for observer in self._observers: observer.update(self, event, data) @property def state(self): return self._state.copy() def update_state(self, key, value): self._state[key] = value self.notify("state_changed", {key: value}) class Logger(Observer): def update(self, subject, event, data): print(f"[Logger] Event: {event}, Data: {data}") class EmailNotifier(Observer): def __init__(self, email): self.email = email def update(self, subject, event, data): if event == "state_changed": print(f"Sending email to {self.email}: State changed to {data}") class Analytics(Observer): def __init__(self): self.events = [] def update(self, subject, event, data): self.events.append((event, data)) print(f"[Analytics] Total events: {len(self.events)}") def report(self): from collections import Counter event_counts = Counter(e[0] for e in self.events) return dict(event_counts) # Usage subject = Subject() logger = Logger() notifier = EmailNotifier("admin@example.com") analytics = Analytics() subject.attach(logger) subject.attach(notifier) subject.attach(analytics) subject.update_state("temperature", 22) subject.update_state("humidity", 65) subject.detach(notifier) subject.update_state("pressure", 1013) print(analytics.report()) # {'state_changed': 3}
observerpub-subevents
Advertisement

๐Ÿ”ฌ Modern Python OOP (3.10-3.12)

๐Ÿ“ Protocol (Structural Typing)

PEP 544 โ€ข static duck typing โ€ข runtime_checkable
from typing import Protocol, runtime_checkable, List from dataclasses import dataclass @runtime_checkable class Drawable(Protocol): def draw(self) -> str: ... @property def area(self) -> float: ... @runtime_checkable class Comparable(Protocol): def __lt__(self, other) -> bool: ... @dataclass class Circle: radius: float def draw(self) -> str: return "โ—‹" @property def area(self) -> float: return 3.14159 * self.radius ** 2 @dataclass class Square: side: float def draw(self) -> str: return "โ–ก" @property def area(self) -> float: return self.side ** 2 class TextDrawable: """No inheritance, just implements required methods""" def __init__(self, text): self.text = text self.area = len(text) # duck typing - area property def draw(self): return f"'{self.text}'" def render_all(shapes: List[Drawable]): for shape in shapes: print(f"{shape.draw()} (area: {shape.area:.1f})") shapes = [ Circle(5), Square(4), TextDrawable("Hello") ] render_all(shapes) # Works for any Drawable-compatible object # Runtime check print(isinstance(Circle(3), Drawable)) # True (with runtime_checkable) print(isinstance(TextDrawable("test"), Drawable)) # True # Generic Protocol class SupportsSum(Protocol): def __add__(self, other): ... def double[T: SupportsSum](x: T) -> T: return x + x print(double(5)) # 10 print(double(3.14)) # 6.28 print(double("abc")) # abcabc
Protocolstructuraltype hints

โ„๏ธ Frozen Dataclasses & NamedTuple

Immutable data โ€ข hashing โ€ข memory efficiency
from dataclasses import dataclass, field from typing import NamedTuple from enum import Enum class Color(Enum): RED = "red" GREEN = "green" BLUE = "blue" @dataclass(frozen=True, slots=True) # Python 3.10+ slots with dataclass class Point3D: """Immutable 3D point with automatic hashing""" x: float y: float z: float = 0.0 def __post_init__(self): # Can't modify frozen instance in __post_init__ # Use object.__setattr__ if needed pass def distance_to(self, other: 'Point3D') -> float: return ((self.x-other.x)**2 + (self.y-other.y)**2 + (self.z-other.z)**2) ** 0.5 def as_tuple(self): return (self.x, self.y, self.z) @dataclass(frozen=True) class ColorPoint: point: Point3D color: Color name: str = field(compare=False) # exclude from equality def __hash__(self): return hash((self.point, self.color)) class City(NamedTuple): """NamedTuple - even lighter than dataclass""" name: str population: int country: str coordinates: tuple[float, float] @property def population_density(self): # Can add properties to NamedTuple area = 100 # dummy area return self.population / area # Usage p1 = Point3D(1, 2, 3) p2 = Point3D(1, 2, 3) print(p1 == p2) # True (automatic __eq__) print(hash(p1)) # Works (frozen=True enables __hash__) cp = ColorPoint(p1, Color.RED, "start") points = {cp} # Can be used in sets/dict keys city = City("Tokyo", 37400068, "Japan", (35.6762, 139.6503)) print(city.name) # Tokyo print(city[0]) # Tokyo (tuple indexing works)
frozenimmutableNamedTuple

๐Ÿ”‘ TypeVar & Generics type-safe containers

PEP 695 (3.12) โ€ข TypeVar โ€ข Generic types
from typing import TypeVar, Generic, Iterator, Optional from collections.abc import Sequence # Python 3.11 and earlier T = TypeVar('T') K = TypeVar('K') V = TypeVar('V') class Stack(Generic[T]): """Generic stack with type safety""" def __init__(self, initial: Optional[list[T]] = None): self._items: list[T] = initial or [] def push(self, item: T) -> None: self._items.append(item) def pop(self) -> T: if not self._items: raise IndexError("pop from empty stack") return self._items.pop() def peek(self) -> T: if not self._items: raise IndexError("peek from empty stack") return self._items[-1] def __len__(self) -> int: return len(self._items) def __iter__(self) -> Iterator[T]: return reversed(self._items) # Python 3.12+ simplified syntax class Pair[T, U]: def __init__(self, first: T, second: U): self.first = first self.second = second def __repr__(self): return f"Pair({self.first!r}, {self.second!r})" def swap(self) -> 'Pair[U, T]': return Pair(self.second, self.first) # Usage stack_int: Stack[int] = Stack() stack_int.push(1) stack_int.push(2) print(stack_int.pop()) # 2 stack_str = Stack[str]() stack_str.push("hello") # stack_str.push(1) # Type checker would error pair = Pair(1, "one") print(pair) # Pair(1, 'one') swapped = pair.swap() print(swapped) # Pair('one', 1) # Multiple type variables class Dictionary[K, V]: def __init__(self): self._data: dict[K, V] = {} def set(self, key: K, value: V) -> None: self._data[key] = value def get(self, key: K) -> Optional[V]: return self._data.get(key) d = Dictionary[str, int]() d.set("age", 30) print(d.get("age")) # 30
GenericTypeVartype hints

๐Ÿงฉ Match Statement with Classes

Python 3.10+ โ€ข structural pattern matching
from dataclasses import dataclass from typing import Any import math @dataclass class Point: x: float y: float @dataclass class Circle: center: Point radius: float @dataclass class Rectangle: top_left: Point bottom_right: Point @dataclass class Line: start: Point end: Point def describe_shape(shape: Any) -> str: match shape: case Point(x, y): return f"Point at ({x}, {y})" case Circle(Point(x, y), r) if r > 0: area = math.pi * r * r return f"Circle at ({x},{y}) radius={r}, area={area:.1f}" case Circle(_, r): return f"Invalid circle with radius {r}" case Rectangle(Point(x1, y1), Point(x2, y2)): width = abs(x2 - x1) height = abs(y2 - y1) return f"Rectangle {width}ร—{height}, area={width*height}" case Line(Point(x1, y1), Point(x2, y2)): length = math.hypot(x2-x1, y2-y1) return f"Line from ({x1},{y1}) to ({x2},{y2}), length={length:.1f}" case _: return f"Unknown shape: {shape}" shapes = [ Point(3, 4), Circle(Point(0, 0), 5), Rectangle(Point(1, 1), Point(4, 5)), Line(Point(0, 0), Point(3, 4)) ] for shape in shapes: print(describe_shape(shape)) # Match with guards and conditions def process_command(cmd): match cmd.split(): case ["quit"]: return "Goodbye!" case ["load", filename] if filename.endswith(('.txt', '.md')): return f"Loading text file: {filename}" case ["load", filename]: return f"Unknown file type: {filename}" case ["save", filename, *rest] if rest: return f"Save with extra args: {rest}" case ["save", filename]: return f"Saving to {filename}" case _: return "Unknown command"
matchpattern matching3.10+
Advertisement

โšก Quick Reference

๐Ÿ“‹ Dunder Methods

  • __init__(self, ...) โ€“ constructor
  • __new__(cls, ...) โ€“ instance creator
  • __del__(self) โ€“ destructor
  • __repr__(self) โ€“ debug representation
  • __str__(self) โ€“ user-friendly string
  • __format__(self, spec) โ€“ formatting
  • __bytes__(self) โ€“ bytes conversion
  • __hash__(self) โ€“ hash for dict keys
  • __bool__(self) โ€“ truthiness
  • __len__(self) โ€“ container length
  • __getitem__(self, key) โ€“ indexing
  • __setitem__(self, key, val) โ€“ assignment
  • __delitem__(self, key) โ€“ deletion
  • __iter__(self) โ€“ iterator
  • __next__(self) โ€“ next value
  • __contains__(self, item) โ€“ membership
  • __call__(self, ...) โ€“ callable object
  • __enter__ / __exit__ โ€“ context manager
  • __add__, __sub__, __mul__ โ€“ arithmetic
  • __eq__, __ne__, __lt__, __gt__ โ€“ comparisons
  • __getattr__, __setattr__ โ€“ dynamic attrs
  • __getattribute__ โ€“ all attribute access
  • __dir__(self) โ€“ dir() listing
  • __slots__ โ€“ memory optimization

๐Ÿงช SOLID Principles

  • Single Responsibility: One class, one job
  • Open/Closed: Open for extension, closed for modification
  • Liskov Substitution: Subtypes must be substitutable for base types
  • Interface Segregation: Many specific interfaces better than one general
  • Dependency Inversion: Depend on abstractions, not concretions
SRP OCP LSP ISP DIP

๐Ÿ“ Design Principles

  • DRY โ€“ Don't Repeat Yourself
  • KISS โ€“ Keep It Simple, Stupid
  • YAGNI โ€“ You Ain't Gonna Need It
  • Composition over Inheritance
  • Program to an Interface
  • Tell, Don't Ask

๐Ÿ”ง Useful Built-ins

  • isinstance(obj, Class) โ€“ type check
  • issubclass(Derived, Base) โ€“ subclass check
  • hasattr(obj, 'attr') โ€“ attribute exists
  • getattr(obj, 'attr', default) โ€“ safe get
  • setattr(obj, 'attr', val) โ€“ set attribute
  • delattr(obj, 'attr') โ€“ delete attribute
  • dir(obj) โ€“ list attributes
  • vars(obj) โ€“ instance __dict__
  • type(obj) โ€“ get class
  • id(obj) โ€“ unique identifier
  • callable(obj) โ€“ check if callable
  • super() โ€“ proxy to parent
  • @property โ€“ getter/setter
  • @classmethod โ€“ class methods
  • @staticmethod โ€“ static methods
  • @functools.total_ordering โ€“ fill comparisons

๐Ÿงฐ Standard Library

  • dataclasses.dataclass
  • abc.ABC, @abstractmethod
  • typing.Protocol
  • enum.Enum
  • functools.wraps
Advertisement

๐Ÿ“˜ Best Practices

  • โœ“ Use dataclasses for data containers
  • โœ“ Prefer composition over inheritance
  • โœ“ Use @property for computed attributes
  • โœ“ Keep classes focused (SRP)
  • โœ“ Use __slots__ for millions of objects
  • โœ“ Write type hints for better IDE support

โš ๏ธ Common Pitfalls

  • โœ— Mutable default arguments
  • โœ— Forgetting super() in inheritance
  • โœ— Deep inheritance hierarchies
  • โœ— Overusing __getattribute__
  • โœ— Not using @wraps in decorators

๐Ÿ“š Recommended Resources


๐Ÿš€ Pro Tips: