The class
class Counter: starts a class, a blueprint for making objects. Python runs the indented body once and builds a class object named Counter that holds everything defined inside it. No Counter object exists yet.
Each def inside the class is a method: a function that belongs to the class. __init__ sets up a new object; add changes one. The first parameter, self, will be the object the method works on.
Make an object
Calling the class like a function makes an instance: a new, empty object of type Counter. Python then calls __init__ automatically, with self set to the new object and start set to 5.
Inside __init__, self.count = start stores an attribute, a name kept inside the object: count is 5. When __init__ ends, the = on line 6 binds the name c to the new object.
Methods at work
c.add(2) looks for add on the object first. The object only holds count, so Python finds add in its class, Counter, and calls it with c as self: the same as Counter.add(c, 2).
Inside add, self is c and n is 2. self.count += n reads count, adds 2 and stores the result back in the same object, so its count changes from 5 to 7.
Counter(0) makes another instance, bound to d, with its own count of 0. Both objects share one class and its methods, but each keeps its own attributes: changing c never touches d.
Line 9 reads c.count and d.count, each from its own object, and print() sends 7 0 to the terminal, one space between them. One blueprint, two objects, two separate states: that is what a class is for.
One rule
Drop self from def add and line 7 fails. Python always passes the object itself as the first argument, so c.add(2) hands add two arguments, c and 2, but add(n) takes only one: TypeError.
Try it yourself
;; step 1/9 · line 1: class Counter: >>> class Counter: ... def __init__(self, start): ... self.count = start ... def add(self, n): ... self.count += n ... >>> Counter <class '__main__.Counter'>
self is only a convention: Python passes the object as the first argument whatever that parameter is called. Every Python programmer still calls it self. The figure shortens the traceback: it leaves out the lines that name the file and show line 7, and wraps the long last line onto two rows. Python 3.10 and later name the method as Counter.add().