Contact
Evil Genius SocietyHomeLearnForumLeaderboardSocialsMessages
← Back to module

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.x shadows 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.