A class is a blueprint; objects are the things built from it. OOP clicks the moment you build something real — so this whole guide builds one game character.
Define with class Name:. The __init__ method runs when you create an object and sets its attributes via self. self is the object itself — Python passes it automatically to every method.
class Player:
def __init__(self, name, hp=100):
self.name = name # attribute — this object's data
self.hp = hp
def take_damage(self, amount):
self.hp -= amount
if self.hp <= 0:
return f"{self.name} is defeated!"
return f"{self.name} has {self.hp} HP left"
hero = Player("Ada") # __init__ runs here
print(hero.name) # Ada
print(hero.take_damage(30)) # Ada has 70 HP leftself is just the object the method was called on. hero.take_damage(30) is secretly Player.take_damage(hero, 30) — Python fills in self for you.
goblin = Player("Goblin", hp=40)
hero = Player("Ada")
# same method, different self → different data:
print(goblin.take_damage(50)) # Goblin is defeated!
print(hero.hp) # 100 — untouchedclass Player:
game = "BitQuest" # CLASS attribute — shared by all
def __init__(self, name):
self.name = name # INSTANCE attribute — unique each
p1, p2 = Player("Ada"), Player("Alan")
print(p1.game, p2.game) # BitQuest BitQuest
p1.name = "Ada the Great" # only changes p1class Wizard(Player): # Wizard IS-A Player
def __init__(self, name, mana=50):
super().__init__(name, hp=80) # run Player's setup too
self.mana = mana
def cast(self, spell):
self.mana -= 10
return f"{self.name} casts {spell}! ({self.mana} mana left)"
gandalf = Wizard("Gandalf")
print(gandalf.cast("fireball")) # inherited + new powers
print(gandalf.take_damage(20)) # Player's method still worksclass Player:
def __init__(self, name, hp=100):
self.name, self.hp = name, hp
def __str__(self):
return f"Player({self.name}, {self.hp} HP)"
hero = Player("Ada")
print(hero) # Player(Ada, 100 HP) — not <object at 0x...>def take_damage(amount): inside a class raises TypeError on call — the first parameter must be selfname = name) creates a useless local, not an attributeself is the specific object a method is acting on. Python passes it automatically: hero.attack() becomes Player.attack(hero).
__init__ is the initializer that runs automatically when you create an object — it's where you set up the object's starting attributes.
A class is the blueprint (Player); objects are individual things built from it (hero, goblin), each with its own attribute values.
When you have data and behavior that belong together — a game character, a bank account, a quiz question. For simple scripts, plain functions are fine.