Classes & Instances
Advanced · 18 min read · ▶ live playground · ✦ checkpoint
A dictionary can hold Mina's health, but it cannot teach itself how to rest. A class is Python's blueprint for a kind of object, and now you turn the house-blueprint idea into real code: make the blueprint, build live objects from it, and give those objects data and behavior.
Define a class
A class statement is the Python syntax that creates a class. The class name usually starts with a capital letter, because Python developers reserve that style for blueprints:
class Player:
pass
mina = Player()
ravi = Player()
mina.name = "Mina"
mina.energy = 5
ravi.name = "Ravi"
ravi.energy = 8
print(f"{mina.name}: {mina.energy} energy")
print(f"{ravi.name}: {ravi.energy} energy")
print(type(mina))Mina: 5 energyRavi: 8 energy<class '__main__.Player'>
class Player: starts the blueprint. pass means "do nothing here yet"; it keeps the empty class body legal while you build up the example.
mina = Player() calls the class and creates an instance, which is one live object built from the class. mina and ravi are two separate houses built from the same Player blueprint.
An attribute is a name attached to an object. mina.name and mina.energy are attributes on the mina object. They are not dictionary keys. They are names reached through the dot.
This works, but it is fragile. If you forget to set ravi.energy, that object is half-built. A class should usually know how to set up each new instance from the start.
Set up each new instance
A constructor is the setup step that runs when you create a new instance. In Python, that setup usually lives in __init__, the method that fills a new instance with starting attributes.
A special method is a method with double underscores on both sides of its name that Python knows to call in a specific situation. __init__ runs after Python creates the new object, before the instance is handed back to your variable.
class Player:
def __init__(self, name, energy):
self.name = name
self.energy = energy
self.mood = "ready"
mina = Player("Mina", 5)
ravi = Player("Ravi", 8)
print(f"{mina.name}: {mina.energy} energy, {mina.mood}")
print(f"{ravi.name}: {ravi.energy} energy, {ravi.mood}")Mina: 5 energy, readyRavi: 8 energy, ready
self is the parameter that receives the current instance. When you call Player("Mina", 5), you pass only "Mina" and 5. Python creates a fresh Player object, passes that object into self, then passes "Mina" into name and 5 into energy.
An instance attribute is an attribute stored on one specific instance. self.name, self.energy, and self.mood belong to whichever player is being set up right now. Mina can be tired while Ravi is ready because each object carries its own state.
Add instance methods
An instance method is a method that works with one specific instance, with self as its first parameter. You define it inside the class, then call it through an object:
class Player:
def __init__(self, name, energy):
self.name = name
self.energy = energy
self.mood = "ready"
def spend_energy(self, amount):
self.energy = self.energy - amount
if self.energy < 3:
self.mood = "tired"
def rest(self, hours):
self.energy = self.energy + hours
self.mood = "steady"
def describe(self):
return f"{self.name}: {self.energy} energy, {self.mood}"
mina = Player("Mina", 5)
ravi = Player("Ravi", 8)
mina.spend_energy(3)
ravi.rest(2)
print(mina.describe())
print(ravi.describe())Mina: 2 energy, tiredRavi: 10 energy, steady
The call mina.spend_energy(3) reads like a sentence: ask Mina to spend 3 energy. Under the hood, Python passes mina into self and 3 into amount.
That is why every instance method needs self first. Without it, the method has no way to know which player's energy to change.
Try it - give the player a tiny routine
Run this, then change the starting energy and the amounts. Watch how each method changes the same object over time.
Class attributes vs instance attributes
A class attribute is an attribute stored on the class itself, shared as a common value by instances. Use class attributes for facts that belong to the blueprint. Use instance attributes for data that can differ from object to object.
class Player:
game = "Crystal Cave"
def __init__(self, name, energy):
self.name = name
self.energy = energy
mina = Player("Mina", 5)
ravi = Player("Ravi", 8)
print(Player.game)
print(mina.game)
print(ravi.game)
print(mina.name)
print(ravi.name)Crystal CaveCrystal CaveCrystal CaveMinaRavi
game is on the class. Every Player can see it because it is true for this whole blueprint. name and energy are on each instance because they are different for each player.
In the house analogy, the class attribute is like writing the builder's name on the blueprint. Every house built from that plan can refer to it. The instance attributes are the details of one real house: who lives there, what color the door is, what is in the kitchen.
For now, keep changing per-object data on self. A player's inventory, health, mood, score, and name should be instance attributes, not class attributes.
Make objects readable
If you print an object without telling Python how it should look, Python falls back to a technical display. Two special methods fix that.
__str__ is the special method that returns friendly text for people. __repr__ is the special method that returns developer-facing text for inspecting an object. The word representation means a text form of a value.
class Player:
game = "Crystal Cave"
def __init__(self, name, energy):
self.name = name
self.energy = energy
def __str__(self):
return f"{self.name} has {self.energy} energy"
def __repr__(self):
return f"Player(name='{self.name}', energy={self.energy})"
mina = Player("Mina", 7)
print(str(mina))
print(repr(mina))
print(mina)
print([mina])Mina has 7 energyPlayer(name='Mina', energy=7)Mina has 7 energy[Player(name='Mina', energy=7)]
print(mina) uses __str__, so it should read nicely. A list display uses __repr__ for each item, so it should help you debug. A good beginner habit: make __str__ pleasant, and make __repr__ precise.
Checkpoint
Answer all three to mark this lesson complete
You can now build the house, set up each new instance, give it methods, and decide how it should print. Next, you make object state harder to misuse, so your classes protect their own rules instead of hoping every caller behaves.