Classes and objects
Bundle state and behaviour together — the pattern behind PyTorch modules, retrievers, and LLM clients.
Why this matters in AI / ML / GenAI
PyTorch models subclass nn.Module. LangChain retrievers, tokenizers, and Hugging Face pipelines are classes you instantiate once and call many times. Understanding __init__, self, and inheritance makes those libraries readable instead of magic.
__init__, self, and attributes
A class is a template. An instance is one object built from it.
__init__ runs at construction and sets up attributes on self. self is just the instance, passed automatically — you write it in the definition, not at the call site.
Load expensive things once in __init__ (a model, a client, an index) and reuse them in methods. That is exactly why Hugging Face pipelines and vector-store clients are classes: constructing is slow, calling is fast.
Methods, __repr__, and __call__
Regular methods take self first. @staticmethod needs no instance; @classmethod receives the class and is commonly used for alternate constructors like Config.from_json(path).
__repr__ controls what you see when you print the object. Add one — debugging a list of nameless objects is miserable.
__call__ makes an instance callable like a function: model(inputs). That is why PyTorch code calls the module directly instead of model.forward(inputs).
Inheritance — use sparingly
A subclass reuses and extends a parent: class BM25Retriever(BaseRetriever):. Call super().__init__(...) to run the parent setup.
Frameworks are built on inheritance (nn.Module), so you must read it. In your own code, prefer composition: a RagPipeline that holds a retriever and a generator is easier to test and swap than a five-level class hierarchy.
Copy-paste examples
Copy into your own editor, or load one into the compiler below and press Run.
A retriever class
Index built once in __init__, reused on every search call.
class KeywordRetriever:
def __init__(self, documents):
self.documents = documents
self.index = {i: set(d.lower().split()) for i, d in enumerate(documents)}
def search(self, query, top_k=2):
terms = set(query.lower().split())
scored = []
for i, words in self.index.items():
overlap = len(terms & words)
if overlap:
scored.append((overlap, self.documents[i]))
scored.sort(reverse=True)
return [doc for _, doc in scored[:top_k]]
def __repr__(self):
return f"KeywordRetriever(n_docs={len(self.documents)})"
retriever = KeywordRetriever([
"python powers machine learning pipelines",
"kubernetes runs containers in production",
"python serves llm apis with fastapi",
])
print(retriever)
for hit in retriever.search("python llm"):
print("-", hit)__call__ makes an object behave like a function
This is why PyTorch code writes model(x) rather than model.forward(x).
class Scaler:
def __init__(self, factor):
self.factor = factor
def __call__(self, values):
return [v * self.factor for v in values]
scale = Scaler(0.5)
print(scale([1.0, 2.0, 3.0]))
print(callable(scale))Inheritance with super()
The subclass reuses parent setup and overrides one method.
class BaseGenerator:
def __init__(self, model_name):
self.model_name = model_name
def generate(self, prompt):
return f"[{self.model_name}] {prompt}"
class CautiousGenerator(BaseGenerator):
def __init__(self, model_name, refusal="I do not know."):
super().__init__(model_name)
self.refusal = refusal
def generate(self, prompt):
if "context:" not in prompt.lower():
return self.refusal
return super().generate(prompt)
gen = CautiousGenerator("local-llm")
print(gen.generate("Context: RAG grounds answers. Question: what is RAG?"))
print(gen.generate("Just guess something"))Build a token-budget tracker
Try it — in-browser Python
Add more calls until the budget is exceeded and see the guard trigger.
Output
Python runs in your browser. First run downloads the runtime.
Press Run (or Ctrl+Enter) to execute.
CPython in WebAssembly. Stdlib works. NumPy and pandas load on demand. No input(), no GPU, no network installs.
Takeaways
- __init__ sets up state once; methods reuse it — the pattern behind model and client classes.
- __call__ is why PyTorch modules are invoked like functions.
- Read inheritance in frameworks, but prefer composition in your own code.