9. Classes & OOP basics¶
Beginner · 9 min read
A class bundles data (attributes) with the functions that work on it (methods). Every SDK
you'll use — OpenAI(), a vector store client, a LangChain chain — is a class.
9.1 A class and its objects¶
class ChatSession:
"""Keeps the message history for one conversation."""
def __init__(self, system_prompt): # runs when you create an object
self.messages = [{"role": "system", "content": system_prompt}] # attribute
def add(self, role, content): # method: a function that belongs to the class
self.messages.append({"role": role, "content": content})
def turns(self):
return len(self.messages) - 1 # don't count the system message
chat = ChatSession("You are concise.") # create an object (an "instance")
chat.add("user", "What is RAG?")
chat.add("assistant", "Retrieval + generation.")
print(chat.turns()) # → 2
print(chat.messages[1]["content"]) # → What is RAG?
self is the object the method was called on — Python passes it automatically.
9.2 Class attributes vs instance attributes¶
class Model:
provider = "openai" # class attribute: shared by every object
def __init__(self, name):
self.name = name # instance attribute: one per object
a, b = Model("gpt-4o-mini"), Model("gpt-4o")
print(a.provider, b.name) # → openai gpt-4o
9.3 A readable __repr__¶
class Chunk:
def __init__(self, text, source):
self.text, self.source = text, source
def __repr__(self): # what print() and the debugger show
return f"Chunk(source={self.source!r}, words={len(self.text.split())})"
print(Chunk("refunds within 30 days", "policy.md")) # → Chunk(source='policy.md', words=4)
9.4 Inheritance¶
class Retriever:
def search(self, query):
raise NotImplementedError # subclasses must provide this
class KeywordRetriever(Retriever): # KeywordRetriever "is a" Retriever
def __init__(self, docs):
self.docs = docs
def search(self, query): # override the parent's method
return [d for d in self.docs if query.lower() in d.lower()]
r = KeywordRetriever(["RAG basics", "Agent loops", "rag evaluation"])
print(r.search("rag")) # → ['RAG basics', 'rag evaluation']
Because every retriever has the same search() method, the rest of your pipeline doesn't care which
one it gets — you can swap keyword search for vector search later.
Why it matters for GenAI
Reading SDK code and docs is mostly reading classes. And wrapping your own pieces (a retriever, a chat session, a tool) in small classes makes them easy to swap and test.
Practice¶
- Add a
last_reply()method toChatSessionthat returns the content of the most recentassistantmessage (orNone).
Answer
class ChatSession:
def __init__(self, system_prompt):
self.messages = [{"role": "system", "content": system_prompt}]
def add(self, role, content):
self.messages.append({"role": role, "content": content})
def last_reply(self):
for m in reversed(self.messages): # newest first
if m["role"] == "assistant":
return m["content"]
return None
chat = ChatSession("Be brief.")
chat.add("assistant", "Hi!")
print(chat.last_reply()) # → Hi!