Explain me detailly about the Oops concept in Python?
Answer
Object-Oriented Programming (OOP) in Python
OOP is a programming paradigm that organises code into objects - combining data (attributes) and behaviour (methods). Python supports OOP natively and uses it heavily across Gen AI frameworks like LangChain, PyTorch, and Hugging Face.
The 4 Pillars of OOP
| Pillar | Definition | Python Mechanism |
|---|---|---|
| Encapsulation | Bundle data + behaviour; restrict direct access | text text |
| Abstraction | Hide complexity, expose only what's needed | text text |
| Inheritance | Child class reuses/extends parent class | text |
| Polymorphism | Same interface, different behaviour | Method overriding, duck typing |
1. Classes & Objects
A class is a blueprint; an object is an instance of that blueprint.
pythonclass LLMModel: # Class variable - shared across all instances provider = "OpenAI" def __init__(self, name: str, max_tokens: int): # Instance variables - unique to each object self.name = name self.max_tokens = max_tokens def describe(self) -> str: return f"{self.name} (max_tokens={self.max_tokens})" def __repr__(self) -> str: return f"LLMModel(name={self.name!r}, max_tokens={self.max_tokens})" def __str__(self) -> str: return self.describe() # Creating objects gpt4 = LLMModel("gpt-4o", 128000) claude = LLMModel("claude-3-5-sonnet", 200000) print(gpt4.describe()) # gpt-4o (max_tokens=128000) print(LLMModel.provider) # OpenAI (class variable) print(repr(gpt4)) # LLMModel(name='gpt-4o', max_tokens=128000)
2. Encapsulation
Control access to internal data using naming conventions and properties.
pythonclass EmbeddingModel: def __init__(self, model: str, api_key: str): self.model = model # public self._endpoint = "https://api.openai.com" # protected (convention) self.__api_key = api_key # private (name-mangled to _EmbeddingModel__api_key) @property def api_key(self) -> str: """Getter - read-only access.""" return "****" + self.__api_key[-4:] @api_key.setter def api_key(self, value: str): """Setter - validate before setting.""" if not value.startswith("sk-"): raise ValueError("Invalid API key format") self.__api_key = value @staticmethod def supported_models() -> list[str]: """Static method - no access to self or cls.""" return ["text-embedding-3-small", "text-embedding-3-large"] @classmethod def from_env(cls, model: str) -> "EmbeddingModel": """Class method - factory pattern using cls.""" import os return cls(model, os.environ.get("OPENAI_API_KEY", "")) embed = EmbeddingModel("text-embedding-3-small", "sk-abc1234") print(embed.api_key) # ****1234 (masked via property) embed.api_key = "sk-xyz9999" # setter validates format
| Access Level | Convention | Accessible From |
|---|---|---|
| Public | text | Anywhere |
| Protected | text | Class + subclasses (by convention) |
| Private | text | Only the defining class (name-mangled) |
3. Inheritance
A child class inherits all attributes and methods from its parent.
pythonclass BaseModel: """Base class for all AI models.""" def __init__(self, name: str, version: str): self.name = name self.version = version def info(self) -> str: return f"{self.name} v{self.version}" def predict(self, prompt: str) -> str: raise NotImplementedError("Subclasses must implement predict()") class ChatModel(BaseModel): """Single-turn chat model.""" def __init__(self, name: str, version: str, temperature: float = 0.7): super().__init__(name, version) # call parent __init__ self.temperature = temperature def predict(self, prompt: str) -> str: return f"[{self.name}] Response to: {prompt[:50]}..." class MultiModalModel(ChatModel): """Chat model that also handles images.""" def __init__(self, name: str, version: str, supports_vision: bool = True): super().__init__(name, version) self.supports_vision = supports_vision def predict_with_image(self, prompt: str, image_path: str) -> str: return f"[{self.name}] Analysing image + prompt..." def info(self) -> str: base = super().info() # call parent method return f"{base} | vision={self.supports_vision}" gpt4o = MultiModalModel("gpt-4o", "2024-11") print(gpt4o.info()) # gpt-4o v2024-11 | vision=True print(gpt4o.predict("Hello")) # [gpt-4o] Response to: Hello... print(isinstance(gpt4o, BaseModel)) # True - inheritance chain print(isinstance(gpt4o, ChatModel)) # True
Multiple Inheritance
pythonclass Loggable: def log(self, msg: str): print(f"[LOG] {msg}") class Cacheable: _cache: dict = {} def cache(self, key: str, value): self._cache[key] = value def get_cached(self, key: str): return self._cache.get(key) class SmartModel(BaseModel, Loggable, Cacheable): """Inherits from multiple parents.""" def predict(self, prompt: str) -> str: cached = self.get_cached(prompt) if cached: self.log("Cache hit!") return cached result = f"[{self.name}] {prompt[:30]}..." self.cache(prompt, result) self.log(f"Cached result for: {prompt[:20]}") return result model = SmartModel("llama-3", "3.1") model.predict("What is RAG?") # computes and caches model.predict("What is RAG?") # Cache hit!
MRO (Method Resolution Order): Python uses the C3 linearisation algorithm to determine which parent method gets called first. Use
to inspect.textClassName.__mro__
4. Abstraction
Define a common interface that all subclasses must implement.
pythonfrom abc import ABC, abstractmethod class VectorStore(ABC): """Abstract base class - cannot be instantiated directly.""" @abstractmethod def add_documents(self, docs: list[str]) -> None: """Must be implemented by every subclass.""" ... @abstractmethod def similarity_search(self, query: str, k: int = 5) -> list[str]: ... def search_and_print(self, query: str) -> None: """Concrete method - shared by all subclasses.""" results = self.similarity_search(query) for i, r in enumerate(results, 1): print(f"{i}. {r}") class ChromaStore(VectorStore): def add_documents(self, docs: list[str]) -> None: print(f"ChromaDB: adding {len(docs)} docs") def similarity_search(self, query: str, k: int = 5) -> list[str]: return [f"chroma_result_{i}" for i in range(k)] class FAISSStore(VectorStore): def add_documents(self, docs: list[str]) -> None: print(f"FAISS: indexing {len(docs)} docs") def similarity_search(self, query: str, k: int = 5) -> list[str]: return [f"faiss_result_{i}" for i in range(k)] # VectorStore() # TypeError: Can't instantiate abstract class store = ChromaStore() store.search_and_print("What is attention?")
5. Polymorphism
The same method name behaves differently depending on the object type.
pythonclass Tokenizer: def tokenize(self, text: str) -> list[str]: return text.split() class BPETokenizer(Tokenizer): def tokenize(self, text: str) -> list[str]: # BPE-specific logic return [text[i:i+3] for i in range(0, len(text), 3)] class WordPieceTokenizer(Tokenizer): def tokenize(self, text: str) -> list[str]: # WordPiece-specific logic return ["##" + w if i > 0 else w for i, w in enumerate(text.split())] def process_text(tokenizer: Tokenizer, text: str) -> list[str]: """Works with ANY tokenizer - polymorphism in action.""" return tokenizer.tokenize(text) tokenizers = [Tokenizer(), BPETokenizer(), WordPieceTokenizer()] for t in tokenizers: print(type(t).__name__, "->", process_text(t, "hello world"))
Duck Typing
Python doesn't require explicit inheritance - if an object has the right methods, it works.
pythonclass LocalLLM: def generate(self, prompt: str) -> str: return f"[Local] {prompt}" class APIModel: def generate(self, prompt: str) -> str: return f"[API] {prompt}" def run_pipeline(model, prompt: str): # No isinstance check needed - just needs a .generate() method return model.generate(prompt) run_pipeline(LocalLLM(), "What is LoRA?") # works run_pipeline(APIModel(), "What is LoRA?") # works
6. Magic (Dunder) Methods
Special methods that define how objects behave with Python operators.
pythonclass TokenBudget: def __init__(self, limit: int): self.limit = limit self.used = 0 def __add__(self, tokens: int) -> "TokenBudget": new = TokenBudget(self.limit) new.used = self.used + tokens return new def __len__(self) -> int: return self.used def __bool__(self) -> bool: return self.used < self.limit def __repr__(self) -> str: return f"TokenBudget(used={self.used}/{self.limit})" def __eq__(self, other: "TokenBudget") -> bool: return self.used == other.used and self.limit == other.limit def __lt__(self, other: "TokenBudget") -> bool: return self.used < other.used budget = TokenBudget(4096) budget = budget + 1200 print(len(budget)) # 1200 print(bool(budget)) # True (budget not exceeded) print(repr(budget)) # TokenBudget(used=1200/4096)
| Dunder Method | Triggered By |
|---|---|
text | text |
text | text |
text | text text |
text | text |
text | text |
text | text |
text | text |
text | text |
text | text |
text | text |
text text | text |
7. Class vs Static vs Instance Methods
pythonclass ModelRegistry: _registry: dict = {} _instance_count = 0 def __init__(self, model_name: str): self.model_name = model_name ModelRegistry._instance_count += 1 def describe(self) -> str: """Instance method - accesses self.""" return f"Model: {self.model_name}" @classmethod def register(cls, name: str, config: dict) -> None: """Class method - accesses cls, used as factory/registry.""" cls._registry[name] = config @classmethod def get_instance_count(cls) -> int: return cls._instance_count @staticmethod def validate_name(name: str) -> bool: """Static method - no access to self or cls.""" return name.isidentifier() and len(name) > 0 ModelRegistry.register("gpt-4o", {"provider": "openai", "tokens": 128000}) print(ModelRegistry.validate_name("gpt-4o")) # False (hyphens not allowed) print(ModelRegistry.validate_name("gpt4o")) # True m = ModelRegistry("llama-3") print(ModelRegistry.get_instance_count()) # 1
8. Dataclasses (Modern OOP)
pythonfrom dataclasses import dataclass, field @dataclass class ModelConfig: name: str temperature: float = 0.7 max_tokens: int = 4096 tags: list[str] = field(default_factory=list) def is_creative(self) -> bool: return self.temperature > 0.8 config = ModelConfig(name="gpt-4o", temperature=0.9, tags=["chat", "vision"]) print(config) # ModelConfig(name='gpt-4o', temperature=0.9, ...) print(config.is_creative()) # True
Pro tip: Pydantic's
is a supercharged dataclass - used extensively in LangChain and FastAPI for validation and serialisation.textBaseModel
OOP in Gen AI - Real-World Pattern
pythonfrom abc import ABC, abstractmethod from dataclasses import dataclass @dataclass class Message: role: str content: str class BaseLLM(ABC): def __init__(self, model: str, temperature: float = 0.7): self.model = model self.temperature = temperature self._history: list[Message] = [] @abstractmethod def _call_api(self, messages: list[Message]) -> str: ... def chat(self, user_input: str) -> str: self._history.append(Message("user", user_input)) response = self._call_api(self._history) self._history.append(Message("assistant", response)) return response def clear_history(self): self._history.clear() class OpenAIChat(BaseLLM): def _call_api(self, messages: list[Message]) -> str: # Real implementation would call openai.chat.completions.create(...) return f"[OpenAI/{self.model}] response" class AnthropicChat(BaseLLM): def _call_api(self, messages: list[Message]) -> str: # Real implementation would call anthropic.messages.create(...) return f"[Anthropic/{self.model}] response" llm = OpenAIChat("gpt-4o") print(llm.chat("What is RAG?")) print(llm.chat("Give an example."))
Quick Reference
| Concept | Key Point |
|---|---|
| Class | Blueprint with text |
| Encapsulation | text text text |
| Inheritance | text text |
| Abstraction | text text |
| Polymorphism | Override methods, duck typing |
| Dunder methods | text text text text |
| Static method | text text text |
| Class method | text text |
| Dataclass | text |
Remember: OOP in Python is the backbone of every major Gen AI library. LangChain's
, PyTorch'stextBaseLLM, and Hugging Face'stextnn.Moduleall use these exact patterns.textPreTrainedModel
Learn more at Python OOP Docs and Real Python OOP Guide.