Skip to content
Theory · Classes · lesson 5 of 11

Class attributes

Let's go back to the blueprint on the wall. Each player's card has its own fields: a name, a score. But something can be written on the blueprint itself too, say "game: chess". There is one such note, and every card made from the blueprint sees it.

A class works the same way. Some data differs for each instance, like a dog's name. Other data is the same for all: every dog says woof. There is no point in writing such a value into each instance in __init__; it is kept in the class itself:

Python
class Dog:
    sound = "woof"

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

    def speak(self):
        return f"{self.name}: {self.sound}"


rex = Dog("Rex")
print(rex.speak())  # Rex: woof
print(Dog.sound, rex.sound)  # woof woof

Let's take the example apart:

  1. sound = "woof" sits in the class body, outside the methods: it is the note on the blueprint.
  2. self.name = name in __init__ writes the name onto the card rex.
  3. rex.speak() reads both its own name and the shared sound: Rex: woof.
  4. Dog.sound and rex.sound give the same value.

Class attribute

A variable assigned a value in the class body outside the methods is a class attribute. There is one for the whole class, and it is read both through the class name and through any instance. Attributes created through self are called instance attributes: each object has its own.

A class attribute holds what is the same for all instances:

  • the sound every dog makes;
  • a unit of measure;
  • a limit, such as the largest number of players.

Where Python looks for an attribute

When a program reads rex.sound, Python looks for the name in two steps:

  1. First among the attributes of rex itself, on the card.
  2. Failing to find it, Python takes the class attribute, the note on the blueprint.

Methods are found the same way: speak is also kept in the class, one for all instances. A class without __init__ can be called with empty parentheses too: it gives an instance with no instance attributes.

Assigning through an instance

The assignment Dog.sound = "bark" changes the class attribute, the note on the blueprint. Every instance with no sound of its own sees the new value. An assignment through an instance does not touch the class: it creates an attribute of that instance.

Python
a = Dog("Ace")
b = Dog("Bo")
Dog.sound = "bark"
print(a.sound, b.sound)  # bark bark
a.sound = "grr"
print(a.sound, b.sound, Dog.sound)  # grr bark bark

Let's go line by line:

  1. Dog.sound = "bark" rewrites the note on the blueprint, and both a and b read bark.
  2. a.sound = "grr" writes sound onto the card a, and the blueprint stays as it was.
  3. Python finds sound on a before the class attribute, so it prints grr, while b and Dog give bark.

The instance's own value keeps hiding the class attribute after later changes to the class: a.sound stays grr whatever Dog.sound becomes. After del a.sound, the instance attribute of a is gone, and a.sound shows the class attribute again.

This is where the blueprint analogy stops working. A real blueprint does not change once the cards are filled in, but a class attribute can be rewritten at any moment. Every instance without its own value sees the new one at once.

Common mistake

A list that each instance should have for itself is made a class attribute:

Python
class Team:
    members = []  # ✗

    def add(self, name):
        self.members.append(name)


red = Team()
blue = Team()
red.add("Ann")
print(blue.members)  # ['Ann']

self.members.append(name) assigns nothing: it finds the single list in the class and changes it. That list is shared by all teams. Create such a list in __init__ with self.members = [], and each instance gets its own.

Interviewers like to show this trap: "Why did a player from one team end up in the other?" The answer: a mutable class attribute is one for all instances.

A number does not cause this mistake. With count = 0 in the class, self.count += 1 reads 0 from the class and writes 1 into a new instance attribute. The class attribute stays unchanged.

Key points

  • A class attribute is set in the class body outside the methods; there is one for all instances.
  • Python looks for an attribute first in the instance, then in the class.
  • An assignment through an instance creates its own attribute and leaves the class unchanged.
  • A mutable list in a class attribute is shared by all instances: create it in __init__.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way