Comet points at two lines in rover.py: rovers_built = 0, and Rover.rovers_built += 1. "We typed these yesterday," she says. "But Pip only has one battery. Whose count is this?"
Wren makes three rovers on his clipboard: Pip, Dot and Moss. "What does the evidence say?" he asks. "Let's print the count after each one."
Nova projects three rovers, each with its own battery, and one counter floating above them all. "How can I help?" she asks. "Some values belong to each object. Some belong to the class."
Comet nods slowly. "Lead programmer, predict the counts."
An instance variable is unique to each object, like _battery. A class variable is shared by every object of the class.
rovers_built is written in the class body, not in __init__, so it belongs to the class. Each new rover adds 1 to it.
# fleet_count.py
from rover import Rover
print(Rover.rovers_built)
pip = Rover("Pip")
dot = Rover("Dot", 2, 3)
print(Rover.rovers_built)
moss = Rover("Moss")
print(Rover.rovers_built)
print(pip.rovers_built) 0 2 3 3
A class variable that holds a list is shared too. Here is a bug Wren found in a tray class.
# tray_bug.py
class Tray:
"""A tray that holds sample labels. This version has a bug."""
labels = []
def __init__(self, name):
self.name = name
def add(self, label):
self.labels.append(label)
a = Tray("Tray A")
b = Tray("Tray B")
a.add("S01")
b.add("S02")
print(a.name, a.labels)
print(b.name, b.labels) Tray A ['S01', 'S02'] Tray B ['S01', 'S02']
A list stored as a class variable is shared by every object, which is usually a mistake. Make the list in __init__ so each object gets its own.
# tray_bug.py, fixed
class Tray:
"""A tray that holds sample labels."""
def __init__(self, name):
self.name = name
self.labels = []
def add(self, label):
self.labels.append(label)
a = Tray("Tray A")
b = Tray("Tray B")
a.add("S01")
b.add("S02")
print(a.name, a.labels)
print(b.name, b.labels) # count_bug.py
class Counter:
made = 0
def __init__(self):
self.made += 1
a = Counter()
b = Counter()
print(Counter.made, a.made, b.made) | Statement | True or false? |
|---|---|
| A class variable is shared by every object of the class. | ? |
| Each rover has its own copy of rovers_built. | ? |
| A list made in __init__ belongs to one object. | ? |
| self.made += 1 changes the class variable made. | ? |
Shared values are useful, and shared lists are a trap. Tomorrow is Trace Friday: change the inside, keep the outside.