2.1.3OOP Fundamentals

Instance attributes vs class attributes

1,850 words8 min readdifficulty · medium5 backlinks

WHY do we even have two kinds?


WHAT they are precisely

class Dog:
    species = "Canis familiaris"      # class attribute (shared)
 
    def __init__(self, name):
        self.name = name              # instance attribute (per object)

HOW Python actually looks attributes up (the key mechanism)

This single asymmetry (read = search up, write = always on instance) explains every tricky behaviour below.

Figure — Instance attributes vs class attributes

Derivation of the "shadowing" behaviour from the lookup rule


The famous trap: mutable class attributes


Common mistakes (Steel-manned)


Recall Feynman: explain to a 12-year-old

Imagine a classroom. There's one whiteboard at the front that everybody shares — if the teacher writes "Homework: page 5" on it, every student sees the same message. That's a class attribute. Each student also has their own notebook. What I write in my notebook is mine alone; yours stays blank. That's an instance attribute. Tricky part: if I want to "change the homework," I can either (a) write a new note in my own notebook (now only I see my version — the whiteboard is unchanged), or (b) walk up and rewrite the whiteboard (everyone's homework changes). Python's self.x = ... is always "write in my notebook"; ClassName.x = ... is "rewrite the whiteboard".


Active recall

Where is a class attribute physically stored?
In the class's namespace (ClassName.__dict__); one shared copy for all instances.
Where is an instance attribute stored?
In that object's own namespace (obj.__dict__); a separate copy per instance.
In what order does Python resolve obj.x on a READ?
Instance __dict__ first, then the class, then base classes via the MRO.
What does obj.x = value always do, regardless of whether x was a class attribute?
Creates/updates x on the INSTANCE; it never modifies the class.
What is "shadowing"?
When an instance attribute hides a class attribute of the same name during lookup, because the instance is searched first.
Why does a mutable class attribute (e.g. tricks = []) get shared across instances?
Because .append() is a read+mutate of the single shared object; without an assignment no per-instance copy is ever created.
How do you fix a shared-mutable-default bug?
Assign a fresh object per instance inside __init__ (self.tricks = []).
How do you change a class attribute for ALL instances at once?
Reassign it on the class itself: ClassName.attr = new_value.
After a.species = "Wolf", what does Dog.species return?
The unchanged original class value — a only added a shadowing instance attribute.
Are class attributes immutable in Python?
No; "class" refers to where they live, not immutability — they can be reassigned via the class name.

Connections

  • OOP Fundamentals
  • Classes and objects
  • The self parameter
  • __init__ constructor
  • Inheritance and MRO
  • Namespaces and scope
  • Mutable vs immutable objects

Concept Map

lives on

lives on

one shared copy

one copy per object

read searches

then falls to

then

always targets

never modifies

produces

instance value hides

saves

Class attribute

Class __dict__

Instance attribute

instance __dict__

Shared by all objects

Owned by single object

Lookup rule

Base classes MRO

Write rule

Shadowing

Memory

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Dekho, simple analogy lo: ek class ek cookie-cutter jaisa hai aur objects us cutter se bani cookies. Kuch cheezein sab cookies me same hoti hain — jaise "sab dogs ki species Canis familiaris hai". Aisi shared cheez ko hum class attribute banate hain, jo sirf ek baar class ke andar likhi jaati hai aur sabhi objects use share karte hain. Lekin kuch cheezein har object ki apni hoti hain — jaise har dog ka apna name. Yeh instance attribute hai, jo self.name = ... se banta hai aur har object ke paas alag copy hoti hai.

Sabse important rule yaad rakho: jab tum koi attribute read karte ho (obj.x), Python pehle instance me dhoondta hai, na mile to class me. Lekin jab tum write karte ho (obj.x = 5), woh hamesha instance par banta hai — class ko kabhi nahi badalta. Isi asymmetry se saari magic samajh aati hai. Agar tum a.species = "Wolf" likhoge to sirf a ka apna copy banega (shadowing), class aur baaki objects untouched rahenge. Agar sabko change karna hai to class par likho: Dog.species = "...".

Ek bada trap hai mutable class attribute ka. Maan lo tricks = [] class me hai aur tum self.tricks.append("roll") karte ho. Yeh assignment nahi hai — yeh ek shared list ko mutate kar raha hai, isliye saare dogs ki tricks me woh add ho jaata hai! Fix simple hai: list ko __init__ me self.tricks = [] karke banao, taaki har object ki apni alag list ho.

Mantra yaad rakho: "Self for self, Class for class" aur "Mutable default? Move it to __init__." Bas yeh do line interview aur real code dono me bachayegi.

Go deeper — visual, from zero

Test yourself — OOP Fundamentals

Connections