B3.1.3 · Fundamentals of OOP for a single class
Static and non-static variables and methods
Some data belongs to one object. Some data belongs to the whole class. Telling them apart is the entire statement.
Explainer
Kestrel Cycles rents out forty bikes. Every bike has its own ID sticker and its own condition, worn brakes on one, brand new tyres on another. But every bike is rented at the same hourly rate. If that rate were stored separately inside all forty bike objects, raising the price would mean updating forty copies and hoping none were missed. Storing it once, at the level of the class itself, means one change updates every bike at once.
An instance variable (non-static) belongs to one individual object. Every object created from the class gets its own separate copy, and changing one object's copy leaves every other object untouched.
A class variable (static) belongs to the class itself, not to any object. Only one copy exists no matter how many objects are created, and every object sees that same single copy.
The words static and class-level mean the same thing here, as do non-static and instance-level. Java writes the keyword static explicitly; Python marks the distinction by where the variable is written, directly inside the class body for a class variable, or on self for an instance variable.
# Kestrel Cycles: what belongs to the class, and what belongs to each bike class Bike: hourly_rate = 5.00 # CLASS variable: one copy, shared by every bike total_bikes = 0 # CLASS variable: counts every Bike ever made def __init__(self, bike_id): self.bike_id = bike_id # INSTANCE variable: this bike only self.condition = "good" # INSTANCE variable: this bike only Bike.total_bikes += 1 # updates the ONE shared copy
The highlighted lines sit directly in the class body, outside any method, which is exactly what makes them class variables. The two self. lines run once per object, giving each bike its own copy.
Explainer
The same split applies to methods
Methods divide the same way, and for the same reason: does this behaviour need to know which particular object it is working on?
An instance method (non-static) receives the object it was called on as its first parameter, self, so it can read and change that specific object's data. It must be called on an object: bike_a.rental_cost(3).
A static method receives no self, so it cannot touch any individual object's data. It is called on the class itself: Bike.raise_rate(6.50). It exists inside the class because it belongs there conceptually, not because it needs an object.
# both live in the Bike class, but only one of them needs a specific bike class Bike: hourly_rate = 5.00 def rental_cost(self, hours): # INSTANCE method: takes self return hours * Bike.hourly_rate @staticmethod def raise_rate(new_rate): # STATIC method: no self at all Bike.hourly_rate = new_rate
raise_rate() changes the shared rate for the whole fleet, so asking "which bike?" would make no sense. rental_cost() is written as an instance method because in a fuller version it would apply a discount based on that bike's own condition.
Explainer
Usage and scope: what each one can reach
Scope is where the syllabus puts real weight, and it is not symmetrical. An instance can see everything; the class level cannot see any individual object.
| Can it reach… | Instance variable / method | Class variable / static method |
|---|---|---|
| Its own object's data | Yes, through self | No, there is no object to look at |
| The shared class data | Yes, through the class name | Yes, that is what it works on |
| How many copies exist | One per object created | Exactly one, always |
| How it is normally called | On an object: bike_a.method() | On the class: Bike.method() |
| When memory is set aside | Each time an object is created | Once, when the class is first loaded |
An instance method can freely use class variables, because the class exists whenever an object does. A static method cannot use instance variables, because it has no idea which object you would even mean. That one-way rule explains most static-related errors.
Worked example
What memory actually holds, step by step
Follow the same Kestrel Cycles class as objects get created and changed. Watch which box changes at each step: the single shared one, or one bike's own.
Step 1. The class is defined. No objects exist yet, but the class variables already do, they were set aside the moment the class loaded.
Step 2. The first bike is created. It gets its own box holding its own bike_id and condition. The shared counter goes up, but notice hourly_rate is not copied into the bike's box.
Step 3. A second bike is created. A second, completely separate object box. The counter rises again, on the same single shared copy.
Step 4. Change one bike's instance variable. Only that bike's box changes. This is the behaviour students expect, and it is the easy half.
Step 5. Change the class variable. One assignment, and every bike in the fleet now quotes the new price, because no bike ever had its own copy to fall back on. This is the half that catches people out.
Explainer
Choosing between them
The syllabus asks specifically when to use an instance variable rather than a class variable. The deciding question is short: would every object always agree on this value?
Real example: self.condition on each Kestrel bike, since one bike's brakes wearing out says nothing about the other thirty-nine.
Advantage
- Each object keeps its own independent state (objects cannot corrupt each other, so a bug in handling one bike cannot silently change the rest of the fleet)
Disadvantage
- Genuinely shared values get duplicated (storing the rate on all forty bikes means forty places to update, and any missed one becomes a pricing bug that is hard to trace)
Real example: Bike.hourly_rate, one price the whole fleet is rented at, and Bike.total_bikes, a count that belongs to no single bike.
Advantage
- One value, updated in exactly one place (a price change is a single assignment that every existing and future object picks up automatically)
Disadvantage
- Every object is affected by any change (code anywhere in the program can change it, and a value that later needs to differ per object requires redesigning the class)
The rule of thumb: use an instance variable by default, and reach for a class variable only when the value genuinely describes the class as a whole, a shared rate, a running count of objects, or a fixed constant. A value that might one day differ per object should start as an instance variable.
Try it yourself
Run the Kestrel Cycles class
The same class from the stepper, live and editable. Try changing bike_a.hourly_rate = 9.00 instead of Bike.hourly_rate, that single character difference creates a brand new instance variable that shadows the shared one for that bike alone, which is exactly the trap in the mistake callout below.
Running in offline fallback mode. The full Python engine couldn't load (blocked school network, or no internet), so this console switched automatically to a small hand-written engine. It runs this page's scenario, including class variables and static methods, but it isn't full Python: one visible difference is that whole-number floats print as 5 here rather than Python's 5.0.
Check your understanding
Two Bike objects exist. A program runs bike_a.hourly_rate = 9.00, then prints bike_b.hourly_rate. What is printed, and why?
Common mistake
B3.1.3RecurringCandidates describe a class variable as being "shared" without stating the point that earns the mark: only one copy exists in memory, so there is nothing per-object to update. Answers that stop at "all objects can use it" describe access, not storage, and read as vague.
The second recurring error is writing to a class variable through an object. In Python this quietly creates a new instance variable that hides the shared one for that object, so the program keeps running and produces wrong values with no error message at all.
Answer frame that scores: say how many copies exist → say who they belong to → state the consequence of changing one.
What a weak answer looks like: “A static variable is shared between all the objects of the class.” True but thin: it never says only one copy exists, and never says what happens when it changes.
What it should say: “A static variable has exactly one copy, stored with the class rather than with any object, so all forty bikes read that same copy. Changing Bike.hourly_rate once changes the price every bike reports, whereas each bike's condition is an instance variable with its own copy per object.” Copies counted, owner named, consequence stated.
Revision questions
Past-paper style. Write an answer, then reveal the marking points and mark yourself.
[2]Distinguish between a static variable and an instance variable.
Marking points
评分要点
- A static (class) variable belongs to the class and there is one shared copy that every instance sees.
- An instance variable belongs to each object separately, so every object has its own independent value.
- 静态(类)变量属于类,只有一份共享的副本,每个实例都看到它。
- 实例变量分别属于每个对象,所以每个对象都有自己独立的值。
[3]A Bike class stores hourly_rate as a class variable. Explain one advantage and one disadvantage of this decision.
Marking points
评分要点
- Advantage: the hourly rate is stored once, so updating it changes the rate for every bike consistently and there is no risk of copies disagreeing.
- Disadvantage: every bike is forced to share the same rate, so if one bike ever needs a different rate the design has to change.
- It also uses slightly less memory than storing the rate on every instance.
- 优点:每小时费率只存一次,所以更新它会一致地改变每辆车的费率,不会出现副本互相矛盾。
- 缺点:每辆车都被迫共享同一费率,所以如果某辆车需要不同的费率,设计就必须改变。
- 它也比在每个实例上存费率略微少用一点内存。
[3]A developer adds a static method to Bike and tries to read self.condition inside it. Explain why this cannot work.
Marking points
评分要点
- A static method belongs to the class, not to any object, so it is not given a self parameter.
- self.condition refers to an instance variable, which exists only on a particular object.
- Inside a static method there is no instance, so there is nothing for self to refer to and the access fails.
- To read condition the method would need to be an instance method (taking self), or be passed a Bike object explicitly.
- 静态方法属于类,不属于任何对象,所以它没有 self 参数。
- self.condition 指的是一个实例变量,它只存在于某个特定的对象上。
- 在静态方法内部没有实例,所以 self 无所指代,访问失败。
- 要读取 condition,该方法需要是一个实例方法(接受 self),或被显式传入一个 Bike 对象。
Flashcards
Active recall for this statement’s key terms.
Keep this statement’s practice with you
Free account: unlock every revision question and flashcard for B3.1.3, and every other IB CS 2027 statement.