Python Class Attributes
Object-Oriented Python Object-Oriented Python

Python Class Attributes vs Instance Attributes

1Python Class Attributes: overview

A class attribute is a variable stored once on the class and shared by every object, while an instance attribute is stored on a single object and belongs only to it; when you read an attribute, Python checks the object first and the class second.

GoalBy the end you can tell class attributes from instance attributes, build class constants and shared counters, predict which value Python finds, avoid the shadowing and shared-list surprises, and inspect where each attribute lives with vars().

Before this lesson: Python Classes and Objects

2Python Class Attributes: diagram

Python Class Attributes vs Instance Attributesrobot.MAX_SPEEDyou read an attributesearch starts hereOn the object?yes: use itno: try the classOn the class?yes: use itno: give upAttributeErrornot found anywhere
Python searches the object first, then its class. Assigning is different: obj.name = value always stores the value on the object, which is why an instance attribute can hide a class attribute of the same name.

3Python Class Attributes: explained step by step

In the last lesson you met one class attribute in passing. This lesson looks at them properly, because the difference between data that belongs to the class and data that belongs to one object is behind some of the most confusing bugs in beginner object-oriented code.

Two places to store data

An attribute can be created in two places:

  • In the class body, directly under class and outside every method. This makes a class attribute: one value, stored once on the class and shared by every object.
  • In __init__, on self. This makes an instance attribute: every object gets its own copy.
Python
class Player:
    game = "Space Race"        # class attribute: one copy, shared

    def __init__(self, name):
        self.name = name       # instance attribute: one copy per object

asha = Player("Asha")
ben = Player("Ben")
print(asha.name, ben.name)
print(asha.game, ben.game, Player.game)

Both players can read game, yet neither of them stores it. The value lives on Player, and every object finds it there.

Class constants

The most common use of a class attribute is a value that is the same for every object and rarely changes: a tax rate, a speed limit, a default size. By convention such names are written in UPPER_CASE. Keeping the value in one place means you change it once, instead of hunting through every method that used a copy of the number.

Python
class Scooter:
    MAX_SPEED = 25

    def __init__(self, name):
        self.name = name
        self.speed = 0

    def accelerate(self, amount):
        self.speed = min(self.speed + amount, self.MAX_SPEED)

kick = Scooter("Kick")
kick.accelerate(20)
kick.accelerate(20)
print(kick.speed, Scooter.MAX_SPEED)

Inside a method you can read the constant as self.MAX_SPEED or as Scooter.MAX_SPEED. Outside the class, both kick.MAX_SPEED and Scooter.MAX_SPEED work.

A counter shared by all objects

A class attribute is also the natural home for facts about the class as a whole, such as "how many objects have been created". Each time __init__ runs, it adds one to the value stored on the class:

Python
class Robot:
    built = 0                  # shared: how many robots exist

    def __init__(self, name):
        self.name = name
        Robot.built += 1       # read Robot.built, add 1, store it back on the class

Robot("R2")
Robot("R3")
Robot("Eve")
print(Robot.built)

Notice that the line uses the class name, not self. The next two sections explain why that matters.

Where Python looks: instance first, then class

When you write obj.speed, Python has to decide where speed lives. It always searches in the same order:

  1. the object's own attributes (what __init__ stored on self),
  2. then the class's attributes,
  3. and if neither has the name, it raises AttributeError.

That one rule explains everything else on this page.

The shadowing surprise

Reading an attribute can fall back to the class. Assigning never does: obj.unit = "F" always stores the value on the object, even if the class already has a unit. The new instance attribute then shadows (hides) the class one, for that object only.

Python
class Thermostat:
    unit = "C"

hall = Thermostat()
attic = Thermostat()
attic.unit = "F"                 # creates a NEW attribute on attic only
print(hall.unit, attic.unit, Thermostat.unit)

Thermostat.unit = "K"            # changes the class attribute itself
print(hall.unit, attic.unit, Thermostat.unit)

After attic.unit = "F", the attic has its own unit and the class still says "C". Changing the class later reaches every object that has not shadowed the name (hall), but not the one that has (attic).

This is also why self.built += 1 would be a bug in the counter above. It means self.built = self.built + 1: Python reads the class value (0), adds one, and stores 1 on the object as a brand-new instance attribute. The shared counter never moves. To change the class attribute, assign through the class: Robot.built += 1.

The shared-list pitfall

Shadowing only happens when you assign. Methods like append() change an existing list in place, so no assignment happens, nothing is shadowed, and every object sees the change. That makes a mutable class attribute (a list, dict or set) a trap when each object is meant to have its own:

Python
class SharedPlaylist:
    songs = []                    # ONE list for every playlist

    def add(self, title):
        self.songs.append(title)

class OwnPlaylist:
    def __init__(self):
        self.songs = []           # a fresh list for each playlist

    def add(self, title):
        self.songs.append(title)

gym, road = SharedPlaylist(), SharedPlaylist()
gym.add("Eye of the Tiger")
print(road.songs)

chill, focus = OwnPlaylist(), OwnPlaylist()
chill.add("Weightless")
print(focus.songs)

The rule of thumb: anything mutable that belongs to one object must be created inside __init__.

Look inside with vars() and __dict__

When you are unsure where an attribute lives, ask Python. vars(obj) (the same dictionary as obj.__dict__) holds only the object's own attributes. The class keeps its attributes in Class.__dict__, which also contains some built-in entries starting with __, so filter those out and sorted() the names to get a tidy, predictable list.

Python
class Dog:
    species = "Canis familiaris"

    def __init__(self, name):
        self.name = name

    def bark(self):
        return f"{self.name} says woof"

rex = Dog("Rex")
print(vars(rex))
print(sorted(key for key in Dog.__dict__ if not key.startswith("__")))
print("species" in vars(rex), rex.species)

The last line is a handy test for shadowing: if a name is in vars(obj), the object owns it; if not, Python is finding it on the class.

Which one should I use?

  • Different for every object (a name, a score, a balance)? Instance attribute, set in __init__.
  • The same for every object (a rate, a limit, a default)? Class attribute, written in UPPER_CASE.
  • A fact about the class as a whole (how many objects exist)? Class attribute, changed through the class name.
  • A list, dict or set that each object should own? Create it in __init__, never in the class body.

4Python Class Attributes: syntax

class Name:
    CONSTANT = 0.2                # class attribute: shared, written in the class body
    count = 0                     # class-level counter

    def __init__(self, x):
        self.x = x                # instance attribute: one per object
        Name.count += 1           # change the class attribute (not self.count!)

obj = Name(1)
obj.x                             # the object's own attribute
obj.CONSTANT                      # not on obj, so Python finds it on the class
Name.CONSTANT                     # read it through the class
obj.CONSTANT = 0.5                # creates a NEW instance attribute (shadows the class one)
Name.count = 10                   # changes it for every object that has not shadowed it
vars(obj)                         # the object's own attributes only
sorted(Name.__dict__)             # the names stored on the class

5Python Class Attributes: run the example

Press Run (or Ctrl + Enter). The output (step 6) appears directly below the code. Change anything and run it again.

Interactive example
class Product:
    TAX_RATE = 0.20
    created = 0

    def __init__(self, name, price):
        self.name = name
        self.price = price
        Product.created += 1

    def price_with_tax(self):
        return round(self.price * (1 + self.TAX_RATE), 2)

pen = Product("Pen", 2.50)
lamp = Product("Lamp", 40)
print(pen.name, pen.price_with_tax())
print(lamp.name, lamp.price_with_tax())
print("Products created:", Product.created)
print("Tax rate:", pen.TAX_RATE, Product.TAX_RATE)
Expected outputPen 3.0
Lamp 48.0
Products created: 2
Tax rate: 0.2 0.2

7Python Class Attributes: how the example works

Hover or tap a step to highlight its lines in the example above.

  1. Line 1class Product: starts the blueprint.
  2. Line 2-3These two names sit in the class body, outside any method, so they are class attributes. TAX_RATE is a constant shared by every product, and created is a counter for the class as a whole. Each exists exactly once, on Product.
  3. Line 5-7__init__ stores name and price on self. These are instance attributes, so every product gets its own pair of values.
  4. Line 8Product.created += 1 uses the class name on purpose, so the shared counter on the class changes. Writing self.created += 1 here would create a separate counter on each object instead.
  5. Line 10-11self.TAX_RATE is not stored on the object, so Python falls back to the class and finds 0.20 there. Prices are rounded to two decimals.
  6. Line 13-14Each call runs __init__ once, so the counter goes from 0 to 1 and then to 2.
  7. Line 15-16Both products use the same tax rate but their own price: 2.50 * 1.2 is 3.0 and 40 * 1.2 is 48.0.
  8. Line 17The counter now reads 2. Reading it through the class name always gives the shared value.
  9. Line 18pen.TAX_RATE and Product.TAX_RATE reach the same value: the object has no copy of its own, so Python looks on the class.

8Python Class Attributes: common mistakes

Setting a class attribute through one object

✗ Wrong

class Server:
    status = "online"

web = Server()
db = Server()
web.status = "offline"
print(db.status)
No error, but it prints online: the change never reached db

✓ Right

class Server:
    status = "online"

web = Server()
db = Server()
Server.status = "offline"
print(db.status)

web.status = ... creates a new attribute on web only and shadows the class one. To change the value for everyone, assign through the class name.

Counting with self.count += 1

✗ Wrong

class Visitor:
    count = 0

    def __init__(self, name):
        self.name = name
        self.count += 1

Visitor("Asha")
Visitor("Ben")
print(Visitor.count)
No error, but it prints 0 instead of 2

✓ Right

class Visitor:
    count = 0

    def __init__(self, name):
        self.name = name
        Visitor.count += 1

Visitor("Asha")
Visitor("Ben")
print(Visitor.count)

self.count += 1 reads the class value and stores the result on the object, so each visitor ends up with its own count of 1 and the class counter never changes. Use the class name instead.

A list in the class body shared by every object

✗ Wrong

class ShoppingCart:
    items = []

    def add(self, item):
        self.items.append(item)

mine = ShoppingCart()
yours = ShoppingCart()
mine.add("milk")
print(yours.items)
No error, but it prints ['milk']: your cart contains my milk

✓ Right

class ShoppingCart:
    def __init__(self):
        self.items = []

    def add(self, item):
        self.items.append(item)

mine = ShoppingCart()
yours = ShoppingCart()
mine.add("milk")
print(yours.items)

The list is created once, when the class is defined, and every cart appends to that same list. Create it in __init__ so each cart gets its own.

Reading a class constant without self. or the class name

✗ Wrong

class Circle:
    PI = 3.14159

    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return PI * self.radius ** 2

print(Circle(2).area())
NameError: name 'PI' is not defined. Did you mean: 'self.PI'?

✓ Right

class Circle:
    PI = 3.14159

    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return self.PI * self.radius ** 2

print(Circle(2).area())

A name written in the class body is not a variable you can use inside methods. Reach it through the object (self.PI) or the class (Circle.PI).

Looking for a class attribute in vars(obj)

✗ Wrong

class Car:
    wheels = 4

    def __init__(self, model):
        self.model = model

van = Car("Van")
print(vars(van)["wheels"])
KeyError: 'wheels'

✓ Right

class Car:
    wheels = 4

    def __init__(self, model):
        self.model = model

van = Car("Van")
print(van.wheels)
print("wheels" in vars(van), "wheels" in vars(Car))

vars(obj) lists only the object's own attributes, and wheels lives on the class. Read it normally with van.wheels, or look in vars(Car).

9Python Class Attributes: exercises

Write your answer, then press Check. The checks run in your browser.

1. Count the robots

Write code Easy

The class Robot should keep track of how many robots have been built.

  1. Add a class attribute built that starts at 0.
  2. Make __init__ add one to it for every new robot. Change the shared counter through the class name, not through self.
  3. Print Robot.built at the end of the program.
Your code
class Robot:
    # 1. add a class attribute called built that starts at 0

    def __init__(self, name):
        self.name = name
        # 2. add one to the shared counter, using the class name

r2 = Robot("R2")
r3 = Robot("R3")
print(r2.name, r3.name)
# 3. print how many robots were built

2. Predict the badge colours

Predict the output Medium

One badge changes its own colour, then the class changes its colour. Type exactly what the program prints.

Read this code
class Badge:
    color = "blue"

a = Badge()
b = Badge()
a.color = "red"
Badge.color = "green"
print(a.color, b.color, Badge.color)

b.color = "gold"
Badge.color = "black"
print(a.color, b.color, Badge.color)

2 more exercises → 6 more examples

Finished this lesson?
Progress is saved in this browser — create a free account to keep it across devices.

Questions learners ask

What is the difference between a class attribute and an instance attribute in Python?

A class attribute is written in the class body and stored once on the class, so every object shares it. An instance attribute is stored on one object, usually with self.name = ... in init, so each object has its own value. When you read obj.name, Python checks the object first and then the class.

Why does my class counter stay at 0 when I write self.count += 1?

self.count += 1 means self.count = self.count + 1. Python reads the class value, adds one, and then stores the result on the object, creating a new instance attribute that hides the class one. The class counter never changes. Write ClassName.count += 1 to update the shared value.

Are class attributes the same as static variables?

They play the same role: one value shared by all objects of the class. Python has no static keyword, so you simply write the variable in the class body. The difference to watch for is that assigning through an object (obj.x = 1) creates a separate instance attribute instead of changing the shared one.

Further reading

10Next lesson

Up nextPython Class Methods and Static MethodsA class method receives the class itself (cls) instead of one object, which makes it ideal for alternative constructors…

Related:
Python Classes and ObjectsPython Class Methods and Static MethodsPython Encapsulation: Public, Protected and Private

← Python Classes and Objects · All Object-Oriented Python lessons · Practise in the IDE