Learn by reading through in order

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")
Where Values Actually Live
Productclass bodytax_rate=0.1(class var)applename='apple'banananame='banana'holdsshared lookupshared lookup
The class variable 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.

Lookup Range — Instance First, Then Class
Product (class variable space)
  • tax_rate = 0.1 ← class variable (shared)
apple (instance variable space)
  • name = 'apple' ← instance variable
banana (instance variable space)
  • name = 'banana' ← instance variable
Python looks for an attribute by checking the instance first, then falling back to the outer class. From the instance's perspective, the class variable acts like a shared umbrella above it.
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

Add a class variable and confirm you can read it from both the class and from instances.

① In class Product:, add a class variable currency = "USD" on its own line (above __init__).

② Write __init__(self, name, price) setting self.name = name and self.price = price.

③ Build apple = Product("apple", 150). Run both print(Product.currency) and print(apple.currency) and confirm they show the same value.

(If you run it correctly, an explanation will appear.)

Python Editor

Run code to see output

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.

Each Instance Owns Its Own Instance Variables
apple(instance)name = 'apple'(separate memory)banana(instance)name = 'banana'(separate memory)holdsholds
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

Use id() to feel that instance variables are truly separate.

① Define class Product: with __init__(self, name, price) setting self.name and self.price.

② Build apple = Product("apple", 150) and banana = Product("banana", 80).

③ Run print(apple is banana) and print(id(apple.name) == id(banana.name)) and confirm both are False.

Python Editor

Run code to see output

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)
What Changes Before vs After Assignment
apple(no instance var)banana(no instance var)Producttax_rate=0.1apple.tax_rate=0.05★ newtax_rate=0.05apple's instance varbanana(no instance var)Producttax_rate=0.1(unchanged)shared lookuplookuplookup
Top row (before): both 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.

Confirm hands-on that self.x = ... creates a new instance variable.

① Define class Product: with class variable tax_rate = 0.1 and __init__(self, name) that sets self.name = name.

② Build apple = Product("apple") and banana = Product("banana").

③ After apple.tax_rate = 0.05, run print(apple.tax_rate), print(banana.tax_rate), and print(Product.tax_rate). Confirm only apple shows the new value while the others stay at 0.1.

Python Editor

Run code to see output

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.

Product.x += 1 vs self.x += 1
Product.x+= 1class x growsto 3running totalworksself.x+= 1each instancebirths a new xclass x staysat 0updateresultassignresult
Assigning through the class lets everyone increment the same single counter. With 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)

Build a counter that increments a class variable by 1 each time an instance is created.

① In class Product:, add a class variable created_count = 0.

② In __init__(self, name), after self.name = name, write Product.created_count += 1 on a single line.

③ Create Product("apple"), Product("banana"), and Product("orange") in turn, then run print(Product.created_count) and confirm it shows 3.

Python Editor

Run code to see output

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.

QUIZ

Knowledge Check

Answer each question one by one.

Q1Where do you write a class variable?

Q2What does the final print output?
class P:
n = 10
a = P()
a.n = 99
b = P()
print(b.n)

Q3To count up the class variable count across all instances, what should you write inside __init__?