Instance variables vs class variables

RoadmapsPython

Overview

Class variables are shared across all instances of a class, whereas instance variables are unique to each specific object. Think of a class variable as a shared whiteboard in a conference room, while instance variables are each employee's personal notebook. When Python looks up an attribute (e.g., `self.name`), it checks the instance's dictionary first. If the attribute is not found, it falls back to the shared class dictionary. Assigning to `self.var_name` always creates or updates an instance variable, even if a class variable with the same name exists. This creates a sharp edge called shadowing, where an instance quietly ignores the shared class state. class Config: timeout = 10 c = Config() c.timeout = 20 # Shadows the class variable print(Config.timeout) # Still 10 This behavior is a common source of bugs when attempting to update shared counters or configuration. The interaction between class state and instances is covered fully in method-types.

It allows you to store state that applies to the entire group without duplicating it in every object. This saves memory and keeps shared state centralized. Common use cases include: - Caching a shared database connection pool - Defining class-level constants - Maintaining a shared counter across all instances

Where used: Django Model fields, SQLAlchemy declarative base, Pydantic schema definitions

Why learn this

Code walkthrough

class Employee:
  company = 'Acme Corp'
  
  def __init__(self, name):
    self.name = name

e1 = Employee('Alice')
e2 = Employee('Bob')
print(e1.company)
print(e2.company)
Employee.company = 'GlobalTech'
print(e1.company)

Focus: `Employee.company = 'GlobalTech'` changes the class variable, affecting the output of `e1.company` since `e1` falls back to the class.

Aha moment

class Server:
  logs = []
  def __init__(self, name):
    self.name = name

s1 = Server('web')
s2 = Server('db')
s1.logs.append('started')
print(s2.logs)

Prediction: What does this print?

Common guess: []

Because `logs` is a class variable, `s1.logs.append()` mutates the single shared list. To give each server its own logs, `self.logs = []` must be assigned in `__init__`.

Common mistakes

Glossary

connection pool
A cache of active database connections that can be reused, saving the time and effort of creating a new connection every time. Example: `engine = create_engine(url, pool_size=5)` creates a pool of 5 reusable connections.
schema definition
A structured plan or blueprint that describes how data should be organized and what types of data are allowed. Example: `class User(BaseModel): name: str; age: int` is a schema definition in Pydantic.

Recall questions

Understanding checks

What will this code print?

['admin']

Because `roles` is a class variable and a mutable `list`, it is shared across all instances. Modifying it via one instance affects the shared `list` seen by all other instances.

If you have a class variable `counter = 0` and you execute `self.counter = 5` inside an instance method, what happens?

It creates a new instance variable named `counter` for that specific instance, shadowing the class variable. The class variable remains `0`.

Assigning directly to `self.attribute` always writes to the instance's dictionary. It does not update the shared class dictionary unless you explicitly use the class name, like `MyClass.counter = 5`.

Practice tasks

Fix the connection counter

The provided `Connection` class tracks connections incorrectly by updating an instance variable instead of the class variable. Modify the `__init__` method so it properly increments the shared `total_connections` class variable.

Challenge

Fix the Shared Cache Bug

The `Route` class below uses a class variable to store cached responses. Unfortunately, every route is sharing the exact same cache dictionary! Fix it by moving the cache to be an instance variable so each route has its own empty dictionary when created.

Continue learning

Previous: Class Creation and Instantiation

Next: Instance, Class, and Static Methods

Return to Python Roadmap