Skip to content

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 to ChatSession that returns the content of the most recent assistant message (or None).
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!