class RunningMean:
def __init__(self):
self.total = 0
self.count = 0
def add(running, value):
running.total += value
running.count += 1
a = RunningMean()
add(a, 55)
add(a, 65)
print(a.total, a.count)120 2
Before this lesson
After this lesson you can
__init__.In review. The maths and code on this page are checked by automated tests on every change, but an independent reader has not signed it off yet. Spot a mistake or a confusing step? Tell us.
Scores arrive one at a time: from a sensor, from a web form, or from a file too large to hold in memory. You want their mean at any moment, without keeping every score. Two numbers are enough, the total so far and the count so far, but they must survive from one score to the next, and a function forgets its local names when it returns (P01.4.3). Where can those two numbers live, and where should the code that updates them go?
From the last lesson
1. What is self inside __init__?
The new instance that is being filled in. Python passes it as the first argument when the class is called.
2. After ben.score = 70, why is ama.score still 55?
ama and ben are two objects, each with its own table. The assignment rebinds score in Ben’s table only.
An object’s attributes last as long as the object does, so they are the place for numbers that must survive between calls. The values of an object’s attributes at a given moment are its state. The functions that change and read that state are written inside the class, where they are called methods.
The data lives in each instance; the methods live once, in the class. a.add(55) finds add in a’s class and calls it with a as self.
The picture holds the two ideas of this lesson.
a has seen two scores and b one, and neither knows about the other.add is not stored in a or in b. When you write a.add(55), Python looks for add in a’s table, does not find it, looks in a’s class, finds the function there, and calls it with a as the first argument.Step 1. A function that updates an object. Start with the class of the last lesson’s shape: __init__ gives each new object a total and a count of 0. A plain function can then update any such object passed to it.
120 2
The numbers survive between the calls because they live in the object, not in the function’s frame.
Step 2. Move the function into the class. Indent it under the class and it becomes a method. By convention its first parameter is renamed self: the object the method works on.
Step 3. Two ways to call it. A method is a function stored in the class, so it can be called through the class, with the object passed by hand. The short form calls it through the object, and Python passes the object for you.
120 2 60.0
a.add(65) is RunningMean.add(a, 65): the object before the dot becomes self, and the arguments in the brackets fill the other parameters.
That is all that self is: the first parameter, which receives the object before the dot. The Python Tutorial puts it the same way: “the call x.f() is exactly equivalent to MyClass.f(x)” (9.3.4).
Step 4. Each instance keeps its own state. Make a second object and give it a different score. The one add updates whichever object it is called through.
A method is a function defined in a class body. Called through an instance, as obj.m(args), it receives that instance as its first parameter, named self by convention. The state of an object is the values of its attributes at a given moment.
Looking up an attribute, obj.name, in two places:
vars(obj);If both fail, Python raises AttributeError. The Tutorial (9.4): “If the same attribute name occurs in both an instance and in a class, then attribute lookup prioritizes the instance.”
Calling a method. When the lookup finds a function in the class, obj.m gives a bound method: the function with obj attached. Calling it puts obj in front of the arguments:
\[ \begin{aligned} &\texttt{a.add(65)}\\ &\quad= \texttt{RunningMean.add(a, 65)} \end{aligned} \]
Two kinds of method. add changes the object’s state and returns None. mean only reads the state and returns an answer, so calling it twice gives the same value. Keeping the two kinds apart makes a program easy to trace: only the calls that change state can make a later answer different.
A method that changes the object, beside a method that only answers. A fitted model has both kinds: fit changes it, predict only answers.
This is how a model in machine learning keeps what it has learned. Calling fit on a scikit-learn model changes its state: it stores the learned numbers in attributes whose names end in an underscore, such as coef_. Calling predict reads them and answers. P01.9.4 builds two such models.
Feed the five scores of the study table into one RunningMean and trace its state after every call. Predict each mean by hand first.
after 55 total 55 count 1 mean 55.0
after 65 total 120 count 2 mean 60.0
after 80 total 200 count 3 mean 66.66666666666667
after 40 total 240 count 4 mean 60.0
after 60 total 300 count 5 mean 60.0
The trace of the worked example. Only total and count are kept; each mean is worked out from them when it is asked for.
The trace shows three things.
The library version is statistics.mean, which needs the whole list at once. The running mean gives the same answer while holding only two numbers, so it can read a file one line at a time.
import pathlib
import statistics
import tempfile
with tempfile.TemporaryDirectory() as folder:
path = pathlib.Path(folder, 'study.csv')
path.write_text('name,hours,score\nAma,2,55\nBen,4,65\nChen,5,80\nDina,1,40\nEli,3,60\n',
encoding='utf-8')
from_file = RunningMean()
with open(path, encoding='utf-8') as file:
for line in file:
if line.startswith('name,'): # the header line
continue
name, hours, score = line.strip().split(',')
from_file.add(int(score))
print(from_file.mean(), statistics.mean(scores))60.0 60
The two agree: 60.0 from the class, and 60 from statistics.mean, which keeps whole numbers whole when it can: the scores are whole and their mean comes out exact (P01.5.1). Only one line of the file is in memory at a time, and the object holds two numbers however long the file is.
Methods are ordinary functions stored in the class, and a bound method remembers its object:
Instance workbench
Choose a score and press a.add or b.add: only that instance’s total and count change, while the methods stay in the class. Then turn on the switch that keeps the scores in a list in the class body, and add scores to a and b again. Whose list grows?
Wrong beside right. A list written in the class body exists once, in the class, and every instance finds it there. A list made in __init__ is made once for each instance.
“A list in the class body gives each instance its own list.”
The class body runs once, when the class is made, so values = [] there makes one list, stored in the class. self.values.append(...) looks up values, does not find it in the instance, finds the class’s list, and changes it. Every instance shares it. The Python Tutorial (9.3.5) shows the same bug with a list of a dog’s tricks that is “unexpectedly shared by all dogs”. Make the list in __init__, with self.values = [].
If the class body said total = 0 and add did self.total += value, the instances would not share a total. self.total += value means self.total = self.total + value: the right side reads the class’s 0, but the assignment then binds total in the instance’s own table. From then on the instance’s own name hides the class’s. self.values.append(value) never assigns to self.values; it changes the one list it finds, which is the class’s.
“print(a.mean) prints the mean.”
Without brackets nothing is called: a.mean is the bound method itself, and print shows something like <bound method RunningMean.mean of ...>. Call it: a.mean().
“A method does not need self if it uses no attributes.”
Python passes the object anyway. A method written def mean(): stops with TypeError when called as a.mean(): RunningMean.mean() takes 0 positional arguments but 1 was given (Python 3.15; the wording varies a little between versions).
“mean() of a new RunningMean gives 0.”
With nothing added, self.total / self.count is \({0 / 0}\), and Python raises ZeroDivisionError. A method should state its rule: check if self.count == 0: and raise a ValueError with a clear message (P01.8.2), as exercise 3 does.
1. Rewrite b.add(80) without the short form.
RunningMean.add(b, 80).
2. Where does Python look when it evaluates a.add, and in what order?
First in the instance’s own table, vars(a); then, if it is not there, in a’s class, where it finds the function add.
3. After a.add(55) and a.add(65), what are a.total, a.count and a.mean()?
120, 2 and \({120 / 2} = 60.0\).
4. Which method of RunningMean changes the object, and which only answers?
add changes the state (total and count) and returns None; mean only reads them and returns the mean.
5. Why do all instances share a list written in the class body?
The class body runs once, so it makes one list, stored in the class. Instances have no values of their own, so self.values finds the class’s list.
1. ★ Give RunningMean a method reset(self) that sets the total and the count back to 0. Is it a method that changes the object or one that only answers?
2. ★★ The mean squared error of predict(hours) = 9 * hours + 33 on the study table is a mean of squared errors (P01.6.2). Compute it with a RunningMean, without building a list. Work out the answer by hand first.
The predictions are 51, 69, 78, 42 and 60, the errors 4, -4, 2, -2 and 0, and their squares 16, 16, 4, 4 and 0, which add up to 40. So the mean squared error is \({40 / 5} = 8.0\).
A training loop reports its loss in exactly this way: a running mean over the batches seen so far.
3. ★★★ Change mean so that, with no values added, it raises ValueError('mean of no values') instead of ZeroDivisionError. Then write a test (P01.8.4) that checks both a usual case and the error case.
The first test builds the state it needs with two calls to add, then checks the answer against \({(55 + 65) / 2} = 60.0\), worked out by hand.
Data lives in each instance; methods live once, in the class.
One class, two states.
def add(self, value): inside the class body.a.add(55) is RunningMean.add(a, 55); self is the object before the dot.add changes them, mean only reads them.__init__.Sources. Each was opened and checked against this lesson on 12 November 2026.
x.f() is exactly equivalent to MyClass.f(x)”; 9.3.5, the Dog class whose tricks list is “unexpectedly shared by all dogs”, fixed with self.tricks = [] in __init__; and 9.4, that “attribute lookup prioritizes the instance”.fit, which returns self, and attributes “estimated or learned from the data must always have a name ending with trailing underscore, for example … coef_”.c.distance(zero), beside its equivalent, Coordinate.distance(c, zero); lecture 18 says that a class “defines data and methods common across all instances”, while data attribute values vary between instances.Next. P01.9.3 gives methods with fixed names, which print, ==, len and + call for you.
This lesson unlocks
P01.9.3 Special methods: objects that act like built-ins