Lesson 4 of 11
Everything stored with self. belongs to one object. A class attribute is defined directly
in the class body and is shared by every instance.
class Dog:
species = "Canis familiaris"
def __init__(self, name):
self.name = name
rex = Dog("Rex")
fido = Dog("Fido")
print(rex.species)
print(fido.species)
print(Dog.species)
name differs per dog — an instance attribute. species is the same for all dogs — a
class attribute. You can read it through any instance, or through the class itself.
When to use each
Use a class attribute for something true of the whole category: a constant, a default, a shared counter, a species name.
Use an instance attribute for anything that varies between objects.
class Circle:
pi = 3.14159
def __init__(self, radius):
self.radius = radius
def area(self):
return Circle.pi * self.radius ** 2
print(Circle(2).area())
Counting instances
A classic use — a class attribute that every instance updates:
class Dog:
count = 0
def __init__(self, name):
self.name = name
Dog.count += 1
Dog("Rex")
Dog("Fido")
Dog("Buddy")
print(Dog.count)
Note Dog.count += 1, not self.count += 1. That distinction matters, and it's the next
section.
The shadowing trap
Reading self.species works — Python checks the instance first, then falls back to the class.
But assigning to self.species creates a new instance attribute that hides the class one for
that object only:
class Dog:
species = "Canis familiaris"
rex = Dog()
fido = Dog()
rex.species = "Wolf"
print(rex.species)
print(fido.species)
print(Dog.species)
Only rex changed. If you meant to change it for everyone, assign through the class:
class Dog:
species = "Canis familiaris"
rex = Dog()
Dog.species = "Dog"
print(rex.species)
This is exactly why the counter above used Dog.count += 1. Writing self.count += 1 would read
the class value, add one, and store the result as a new instance attribute — every dog would
end up with its own count of 1, and Dog.count would stay 0.
Mutable class attributes
The same trap as mutable default arguments in lesson 4.3:
class Dog:
tricks = []
def __init__(self, name):
self.name = name
rex = Dog("Rex")
fido = Dog("Fido")
rex.tricks.append("sit")
print(fido.tricks)
fido knows a trick nobody taught him — .append() mutates the one shared list rather than
rebinding anything. Mutable per-object data belongs in __init__:
class Dog:
def __init__(self, name):
self.name = name
self.tricks = []
rex = Dog("Rex")
fido = Dog("Fido")
rex.tricks.append("sit")
print(rex.tricks)
print(fido.tricks)
Try it yourself
Give Dog a shared species and a per-dog name, and count how many were created.
Checkpoint
- Class attributes are defined in the class body and shared by all instances.
- Instance attributes are set with
self.and unique per object. - Reading falls back to the class; assigning to
self.xshadows it for that object only. - Update shared state through the class:
Dog.count += 1. - Never put a mutable value in a class attribute — build it in
__init__.
Questions about this lesson
Log in to ask a question.