Q1Where do you write a class variable?
Class Variables vs Instance Variables — Where Values Actually Live
A diagram-rich walkthrough of Python class variables vs instance variables. See what's shared, what's per-instance, and what self.x = ... is really doing.
Last time we wrapped up using __init__ practically — required arguments, default arguments — and met the matching __del__. This time, we'll dig into the two kinds of variables in a class — class variables and instance variables — and see what self.x = ... actually does at the memory level.
Two Kinds of Variables
Variables you write in a Python class fall into two main kinds:
- Class variables — written directly under class Product:. Shared by every instance.
- Instance variables — written as self.x = ... inside a method. Owned by each instance individually.
Both are read with the same apple.name dot syntax, so you can't tell them apart by sight. That's exactly why you need to be deliberate about "where the value actually lives."
class Product:
tax_rate = 0.1 # class variable (shared by all)
def __init__(self, name):
self.name = name # instance variable (per instance)
apple = Product("apple")
banana = Product("banana")
tax_rate lives in one place on Product — both apple and banana look at the same spot. The instance variable name is stored in each instance's own memory.Class Variables — One Value Shared by All
Write a class variable directly under class Product:, like tax_rate = 0.1. The key part is that it lives outside any method. The class itself owns one copy, and every instance built from that class shares it.
You can read it as Product.tax_rate (via the class) or apple.tax_rate (via an instance). For the latter, Python does the lookup in this order: first check the instance, then fall back to the class.
- tax_rate = 0.1 ← class variable (shared)
- name = 'apple' ← instance variable
- name = 'banana' ← instance variable
class Product:
tax_rate = 0.1 # class variable (shared by all)
def __init__(self, name, price):
self.name = name # instance variable (per instance)
self.price = price
apple = Product("apple", 150)
banana = Product("banana", 80)
print(Product.tax_rate) # 0.1 (via the class)
print(apple.tax_rate) # 0.1 (also readable through an instance)
print(banana.tax_rate) # 0.1
# Same memory location
print(id(apple.tax_rate) == id(Product.tax_rate)) # True
Instance Variables — Owned by Each Instance
An instance variable is created the moment you write self.x = ... inside a method, as an attribute exclusive to that instance. Most often you initialize them in __init__ with something like self.name = name, but writing self.x = ... in any other method also creates a new instance variable at that moment.
Because each instance stores instance variables in its own memory, changing apple.name has no effect at all on banana.name.
apple.name and banana.name live in different memory locations. Rewriting apple.name doesn't affect banana.name at all.class Product:
def __init__(self, name, price):
self.name = name
self.price = price
apple = Product("apple", 150)
banana = Product("banana", 80)
print(apple.name, apple.price) # apple 150
print(banana.name, banana.price) # banana 80
# id() shows they point to different places
print(id(apple.name) == id(banana.name)) # False
self.x = ... Creates an Instance Variable
Suppose Product has a class variable tax_rate = 0.1, and you write apple.tax_rate = 0.05 for a specific instance — what happens?
The answer: the class variable is not touched, and a brand new instance variable tax_rate is created on apple. From then on, apple.tax_rate returns 0.05 because Python finds it on the instance first and never falls back to the class. banana is completely unaffected.
class Product:
tax_rate = 0.1
def __init__(self, name, price):
self.name = name
self.price = price
apple = Product("apple", 150)
banana = Product("banana", 80)
# Give apple alone a new tax rate
apple.tax_rate = 0.05
print(apple.tax_rate) # 0.05 (apple's instance variable)
print(banana.tax_rate) # 0.1 (falls back to class variable)
print(Product.tax_rate) # 0.1 (class variable unchanged)
apple and banana share a lookup of tax_rate=0.1 on the class. Bottom row (after apple.tax_rate = 0.05): a new instance variable is born on apple, and only apple reads from it. banana and the class are untouched.Don't Confuse This with "Updating a Class Variable Through an Instance"
apple.tax_rate = 0.05 looks at first glance like "updating the class variable," but it's actually just creating a new instance variable on apple. To change the shared class variable itself, you need to assign through the class: Product.tax_rate = 0.05.
When You Want to Update a Class Variable
When you need to update state the class is keeping for everyone — like "change the tax rate for all instances at once" or "track the cumulative number of products created" — you assign through the class variable.
Below, Product.created_count lives on the class itself and represents "the number of products created so far." Writing Product.created_count += 1 inside __init__ updates the same single spot no matter which instance is being created, so the running total stays correct. If you wrote self.created_count += 1 instead, each instance would get its own variable, defeating the original purpose of counting.
self.x += 1, each instance ends up with its own copy, and the shared counter stays at 0.class Product:
created_count = 0 # class variable: cumulative product count
def __init__(self, name):
self.name = name
Product.created_count += 1 # update through the class
apple = Product("apple")
banana = Product("banana")
orange = Product("orange")
print(Product.created_count) # 3
print(apple.created_count) # 3 (also readable via an instance)
We sorted out the two kinds of variables in a class, confirmed that self.x = ... always creates a new instance variable, that instance variables shadow class variables when present, and that shared state is updated through the class.
Knowledge Check
Answer each question one by one.
Q2What does the final print output?class P: n = 10a = P()a.n = 99b = P()print(b.n)
Q3To count up the class variable count across all instances, what should you write inside __init__?