20 Objects
20.1 The Wall
AI used class and self everywhere and you had no idea why. It wrote self.name, self.history, self.respond(), every variable and function had self stuck on the front. You asked AI why and got a lecture about “object-oriented programming” and “encapsulation” that did not help you understand the actual code.
You tried changing self.name to just name and everything broke. You did not know why self was necessary or what it represented.
This chapter fixes that.
20.2 Thinking Session
20.2.1 Getting Oriented
Explain Python classes and objects to me without jargon. I see AI using class Chatbot: and self.name constantly. What does self actually mean? Why can I not just use regular variables and functions? Give me a concrete example where a class is clearly better than functions.
Your AI should explain: a class bundles data (attributes) and behaviour (methods) together. self refers to the specific instance, if you create two chatbots, self.name is different for each one. Without classes, you would need separate variables for each chatbot’s name, history, and settings. If your AI starts with abstract theory about “blueprints” and “objects,” ask for a concrete example first.
20.2.2 Go Deeper
Walk me through the parts of a Python class: __init__, methods, attributes, and the self parameter. Why does every method take self as the first parameter? What is __init__ and when does it run? Show me with a simple example, not a theoretical one.
__init__ is called automatically when you create an instance (bot = Chatbot("PyBot")). self is the instance being created or operated on. Python passes it automatically. Attributes set in __init__ (like self.name = name) persist on the object. Methods are functions that operate on the object’s data.
What is inheritance in Python? AI sometimes writes class EnhancedBot(Chatbot):, what does the parentheses part mean? When would I use inheritance versus just adding methods to the original class?
20.2.3 Challenge It
What is wrong with this class?
class Counter:
count = 0
def increment(self):
count += 1
def get_count():
return countThree bugs: (1) increment uses count instead of self.count. It tries to modify a local variable that does not exist. (2) get_count is missing self parameter. It cannot be called as a method. (3) count = 0 at the class level is a class variable shared by all instances. It should be self.count = 0 in __init__ for per-instance counting.
20.2.4 What You Should Have Learned
- A class bundles data (attributes) and behaviour (methods)
selfrefers to the specific instance__init__sets up the instance when created- Every method takes
selfas its first parameter - Inheritance lets you extend existing classes
- Use classes when you need multiple instances with their own state
20.3 The Gap
You now understand why AI uses classes. When you see class Chatbot: with self.history and self.respond(), you know that self.history belongs to that specific chatbot instance, and respond() operates on that instance’s data. Classes are how AI structures any program that needs multiple instances of the same thing.
In the final Building Session, you will refactor your entire chatbot into a class.
20.4 Building Session
20.4.1 The Spec
Refactor your chatbot into a class:
- A
Chatbotclass with attributes for name, version, history, keywords, responses - Methods:
greet(),classify(),respond(),save_history(),load_history() - An
__init__method that sets up all state - A
run()method that contains the main loop - The entry point creates an instance and calls
run()
20.4.2 Prompt It
Refactor my chatbot to v2.0 using a class. Create a Chatbot class that:
- init(self, name, version) sets up: self.name, self.version, self.history (loaded from file), self.responses (the dictionary), self.keywords (the dictionary), self.message_count, self.debug_mode
- greet(self), displays the welcome screen
- get_user_name(self), asks for and validates the user’s name
- classify(self, text), classifies input using self.keywords
- respond(self, user_input), returns (response, should_exit)
- save_history(self), saves self.history to JSON
- load_history(self), loads history from JSON
- run(self). The main conversation loop
Entry point: bot = Chatbot(“PyBot”, “2.0”) bot.run()
20.4.3 Read the Code
Your AI will produce something like this:
"""PyBot v2.0: Object-oriented chatbot."""
import json
import random
class Chatbot:
"""A terminal chatbot with memory and
pattern matching."""
def __init__(self, name, version):
"""Initialise the chatbot."""
self.name = name
self.version = version
self.message_count = 0
self.debug_mode = False
self.history = self.load_history()
self.responses = {
"greeting": ["Hello!", "Hi there!"],
"farewell": ["Goodbye!", "See you!"],
"question": ["Good question!"],
"default": ["Interesting!", "Go on..."],
}
self.keywords = {
"hello": "greeting",
"hi": "greeting",
"bye": "farewell",
}
def classify(self, text):
"""Classify input into a response category."""
words = text.lower().split()
for word in words:
if word in self.keywords:
return self.keywords[word]
if "?" in text:
return "question"
return "default"
def respond(self, user_input):
"""Generate a response."""
category = self.classify(user_input)
responses = self.responses.get(
category, self.responses["default"]
)
return random.choice(responses), category == "farewell"
def load_history(self):
"""Load conversation history from file."""
try:
with open("chat_history.json") as f:
return json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
return []
def save_history(self):
"""Save conversation history to file."""
with open("chat_history.json", "w") as f:
json.dump(self.history, f, indent=2)
def run(self):
"""Main conversation loop."""
print(f"Hello! I'm {self.name} v{self.version}.")
user_name = input(f"{self.name}: Your name? ").strip()
print(f"Nice to meet you, {user_name}!")
try:
while True:
user_input = input(f"{user_name}: ").strip()
if not user_input:
continue
self.message_count += 1
response, should_exit = self.respond(user_input)
print(f"{self.name}: {response}")
self.history.append({
"user": user_input,
"bot": response,
})
if should_exit:
break
except KeyboardInterrupt:
print(f"\n{self.name}: Interrupted!")
finally:
self.save_history()
if __name__ == "__main__":
bot = Chatbot("PyBot", "2.0")
bot.run()All state lives on self: self.history, self.keywords, self.message_count. Methods access this shared state through self. You could create two chatbots with different names: bot1 = Chatbot("PyBot", "2.0") and bot2 = Chatbot("Helper", "1.0"). Each has its own state. The if __name__ == "__main__" guard means this file works both as a script and as an importable module.
20.4.4 Stretch It
Create an EnhancedBot class that inherits from Chatbot and adds a mood system. Override the respond() method to adjust responses based on self.mood. Call super().respond() to get the base response, then modify it.
20.5 Your Chatbot So Far
- Ch 1-18: Full features split across functions and modules
- Ch 19: Package structure
- Ch 20: Refactored into a Chatbot class with all features
Your chatbot journey is complete. You started with 10 lines of print statements and built a full-featured conversational program with pattern matching, persistent memory, error handling, tests, and a clean class-based architecture.
More importantly, you built it through conversation with AI, directing it, questioning it, and understanding every line it produced.
20.6 Quick Reference
# Define a class
class Dog:
def __init__(self, name, breed):
self.name = name
self.breed = breed
def speak(self):
return f"{self.name} says Woof!"
# Create instances
dog1 = Dog("Rex", "Labrador")
dog2 = Dog("Bella", "Poodle")
print(dog1.speak()) # "Rex says Woof!"
# Inheritance
class Puppy(Dog):
def __init__(self, name, breed, age_months):
super().__init__(name, breed)
self.age_months = age_months
def speak(self):
return f"{self.name} says Yap!"
# Special methods
class Item:
def __str__(self):
return "display string"
def __repr__(self):
return "debug string"
def __len__(self):
return 42