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:
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 woofLet's take the example apart:
sound = "woof"sits in the class body, outside the methods: it is the note on the blueprint.self.name = namein__init__writes the name onto the cardrex.rex.speak()reads both its own name and the shared sound:Rex: woof.Dog.soundandrex.soundgive 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:
- First among the attributes of
rexitself, on the card. - 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.
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 barkLet's go line by line:
Dog.sound = "bark"rewrites the note on the blueprint, and bothaandbreadbark.a.sound = "grr"writessoundonto the carda, and the blueprint stays as it was.- Python finds
soundonabefore the class attribute, so it printsgrr, whilebandDoggivebark.
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:
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.