2.1.1 · D4OOP Fundamentals

Exercises — Class vs object — blueprint vs instance

2,269 words10 min readBack to topic

This page drills the one idea from the parent note: a class is a blueprint, an object is a real thing built from it. We use the same Dog blueprint throughout so nothing new is smuggled in.

Figure — Class vs object — blueprint vs instance

Level 1 — Recognition

Can you name the parts and read the code?

Recall Solution 1.1
  • Class: Dog — the blueprint on the right of the call.
  • Object: d1 — the variable now holding the real thing that got built.
  • The act: instantiation — calling the class like a function to stamp out one object. Why: Dog(...) is not "using a dog", it is "make a new dog from the drawing".
Recall Solution 1.2
  • speciesclass attribute (defined in the class body, outside any method; one shared copy).
  • nameinstance attribute (written via self.name = ..., one copy per object).
  • ageinstance attribute (same reason). Rule of thumb: if it is written with self. inside __init__, it lives on the object.
Recall Solution 1.3

False. Defining a class only produces a blueprint — zero objects exist until you call Dog(...). A drawing is not a house.


Level 2 — Application

Run the blueprint in your head and predict output.

Recall Solution 2.1
Rex says Woof!
Fido says Woof!

Why: bark is stored once on the class, but each call receives a different self (d1 then d2), so the same code reads different data. See the figure: one method box, two arrows pulling from two data boxes.

Recall Solution 2.2

Prints 4 5. d1.age = 4 writes into d1's own memory box only. d2 has a separate box for age, so it still holds 5. This is independent instance state.

Recall Solution 2.3

Both print Canis. Dog.species reads the class copy directly; d1.species finds no instance species, so Python looks it up on the class and finds the same Canis.


Level 3 — Analysis

Reason about identity, sharing, and where changes land.

Recall Solution 3.1
  • a == bFalse. With no custom __eq__, == falls back to identity, so it behaves like is.
  • a is bFalse. Two separate Dog(...) calls made two objects in two memory cells. Same blueprint + same data does not make one object. See Identity vs Equality (is vs ==).
Recall Solution 3.2

Prints Lupus Lupus. We changed the single class copy; both objects look up that same slot, so both see the new value. No instance memory changed at all — see Memory model and references.

Recall Solution 3.3

Prints Wolf Lupus.

  • d1.species = "Wolf" creates a new instance attribute on d1 that shadows the class one. Now d1 has its own species.
  • Dog.species = "Lupus" changes the class copy.
  • d1.species finds its own shadow → Wolf. d2 has no shadow → falls back to class → Lupus. Picture: in the figure, d1 grew a private species box; the lookup stops there and never reaches the class.

Level 4 — Synthesis

Combine the rules to build and predict multi-step programs.

Recall Solution 4.1

Prints 3. Each instantiation runs __init__, and each __init__ bumps the one shared Dog.count. Because we wrote Dog.count += 1 (class), not self.count += 1 (which would make instance shadows), all three calls accumulate into the same slot.

Recall Solution 4.2

Prints 1 1 0.

  • self.count += 1 means self.count = self.count + 1. The right side reads (finds class 0), the left side writes an instance box = 1.
  • So each object gets its own count = 1; the class count is never touched → stays 0. This is the same read/write asymmetry from Exercise 3.3, applied to counting.
Recall Solution 4.3

Prints 9 True. b = a copies the reference, not the object — both names point at the same memory cell. Editing through b is editing the one object, so a.age also shows 9, and a is b is True. Contrast Exercise 3.1 where two Dog(...) calls made two objects.


Level 5 — Mastery

Design the blueprint yourself; reason about all cases.

Recall Solution 5.1
  • After x.add_interest(): x.balance = 100 + 100·0.05 = 105. y untouched → 200. First print: 105.0 200.
  • Then rate becomes 0.10. y.add_interest(): y.balance = 200 + 200·0.10 = 220. Second print: 220.0. Design lesson: balance is per-object (own state → instance attribute), interest_rate is bank-wide (shared policy → class attribute). Choosing which is which is the class-vs-object skill.
Recall Solution 5.2
  • Line 1: Wolf Canis Canis. p grew a shadow species; q and the class are unchanged.
  • Line 2: True. Both names hold the string "Rex", and == on strings compares value.
  • Line 3: False. Two Dog(...) calls → two distinct objects. Equal data (line 2) does not imply same object (line 3): value vs identity, cleanly separated.
Recall Solution 5.3

The cutter carries the shape and instructions (behaviour) — you need only one, and every cookie is stamped by the same one; so a method lives once on the class and each call just passes a different self. The toppings (data like balance) differ per cookie, so each object needs its own box. Naive alternative — copying the method's code into all 1000 objects — would store 1000 identical copies of add_interest, wasting memory for zero benefit, since the code is the same for all. Storing methods once is the whole reason the blueprint/instance split saves memory.


Recall One-line self-test before you leave

Read is a search (instance → class); write obj.x = ... always lands on the instance; a new object exists only after calling the class; is compares cells, == (by default) also compares cells until you define __eq__.

Connections

  • Constructors and __init__ — the __init__ these exercises lean on.
  • self and instance methods — why self routes one method to many objects.
  • Class vs instance attributes — the shadowing in 3.3 and 4.2.
  • Identity vs Equality (is vs ==) — Exercises 3.1 and 5.2.
  • Memory model and references — Exercise 4.3's reference copy.
  • Encapsulation — the design choices in Level 5.