Unit 12 · lesson
Follow One Object Through the System
Core path: 30 minutes
Object-oriented code gets confusing when students treat the class, the instance, the saved dictionary, and the JSON record as if they are all the same thing.
They are related. They are not identical.
This lesson follows one player all the way through the architecture.
Start with the class
class Player:
def __init__(self, name, score):
self.name = name
self.score = score
self.rank = self.determine_rank()
The class is the definition of what a Player object should know and what behavior it can provide.
At this point, there is no specific player named Nova yet.
Create an instance
nova = Player("Nova", 850)
Now one instance exists.
Trace the initializer:
name argument = "Nova"
score argument = 850
↓
self.name = "Nova"
self.score = 850
↓
self.determine_rank()
↓
self.rank = "ADVANCED"
The class did not "turn into" Nova. The class was used to create a particular object with its own state.
Prove that two instances are independent
nova = Player("Nova", 850)
maya = Player("Maya", 960)
nova.add_score(100)
After the call:
Nova → score 950
Maya → score 960
self inside nova.add_score(...) refers to Nova's instance.
If Maya changes too, something is wrong with the design or the way shared state was created.
Follow one method call
nova.add_score(100)
The call crosses several steps:
caller selects nova
↓
method receives self = nova
↓
self.score changes
↓
rank is recalculated
↓
control returns to caller
That is a state transition on one object.
Use the debugger and stop inside the method. Inspect self rather than only looking at the final printed score.
Now cross the persistence boundary
Your live object might look conceptually like:
Player instance
name = Nova
score = 950
rank = ELITE
methods available
But JSON needs ordinary data.
So:
nova.to_dict()
produces something like:
{
"name": "Nova",
"score": 950,
}
The methods are not saved.
The class definition is not saved into the JSON record.
The file stores enough data to reconstruct the object later.
Stop the process
After saving, terminate the program.
The live nova object disappears with the process.
The JSON remains:
{
"name": "Nova",
"score": 950
}
On the next run:
record = {"name": "Nova", "score": 950}
player = Player(record["name"], record["score"])
creates a new Player instance from the persisted data.
It represents the same logical player record, but it is not the same runtime object that existed before shutdown.
That distinction matters.
Derived state can become a trap
Suppose you save:
{
"name": "Nova",
"score": 950,
"rank": "ROOKIE"
}
But your current rank rules say 950 should be ELITE.
Which value should the program trust?
If rank is completely derived from score, storing both values creates a synchronization problem.
A cleaner design may store the authoritative value:
score
and recalculate:
rank
when the object is reconstructed.
That is a source-of-truth decision, not an OOP syntax rule.
Trace your own roster
Pick one real player from Lesson 4 and document:
class definition involved:
constructor arguments:
instance attributes after creation:
method call that changes state:
attributes after method call:
to_dict() result:
JSON representation:
values used during reload:
new instance state after reload:
Then answer:
Which parts belong to the Player model, which belong to storage, and which belong to presentation?
If the answer is "everything belongs in the class because classes are OOP," you have missed the architecture lesson.
The goal is not to turn the whole program into objects.
The goal is to make ownership visible.
Reader workbench
Trace instance state and behavior
This is the Unit's one-file practice surface. Read the code, predict one result, run it, then change a value, input, condition, or boundary and explain why the evidence changed. Multi-file projects, Git, terminals, packages, and live services still belong in the full development workspace.
Run the code to see output.
This runs in an isolated Python worker in your browser. Your edits stay in this browser until you reset them.