Definition
A program rarely handles a single account or a single user. It deals with dozens of them, alike in shape and different in their values. One variable per account quickly becomes unworkable: at fifty customers, nobody keeps track any more.
Hence the split between the class and its instances. The class describes what an object holds and can do, but it holds nothing itself: it is a blueprint. The instance is the copy built from that blueprint, living in memory with its own values. An Account class holds nobody's money: it is each account opened that carries a balance.
The example below creates two accounts from that class, then credits the first one. Watch what the second one prints.
class Account:
def __init__(self, owner):
self.owner = owner # a value belonging to this object
self.balance = 0
first = Account("Camille") # one instance
second = Account("Sofia") # another one, independent
first.balance = 120
print(second.balance) # 0: the second one saw nothing happenBoth accounts come from the same blueprint and yet share nothing. Crediting the first leaves the second untouched: that is what defines an instance, values of its own. A thousand accounts can coexist without ever stepping on each other.
What belongs to the instance, what belongs to the class
What remains is where to put a value, because Python offers two places that look much alike on the page. An attribute written in the body of the class is created once, when the file is loaded, and every object refers to it. An attribute placed on self is rebuilt on every construction: there are as many of them as objects.
The table puts the two forms side by side, last column first: what separates them comes down to use, not to syntax.
| Writing | Where the value lives | Consequence |
|---|---|---|
fee = 2 in the class | On the class | A single value for the whole program |
self.balance = 0 in __init__ | On the instance | One value per object created |
history = [] in the class | On the class | The same list for every object |
The third row is the trap, invisible when re-reading the code. A mutable collection declared at class level is built once, when the file is loaded: every object appending an operation writes into everyone else's. Without a single error, the history of the first customer ends up holding the movements of the last one.
The two lines below are easily mistaken for one another, yet only one gives each object a history of its own.
class Account:
history = [] # shared by all: almost always a mistake
def __init__(self):
self.history = [] # rebuilt for each instance: what you wantA list or a dictionary written at class level exists in a single copy for the whole program, and the failure surfaces much later, often in production. A value that changes from one object to the next is written on self, inside __init__.
What Python does when the class is called
Writing Account("Camille") looks like a function call, and it is tempting to think it merely runs the initialisation method. A little more happens: __new__ builds the empty object, then __init__ fills it. The first step creates the instance, the second gives it its content. The nuance only shows on special cases, such as an immutable object, to be prepared before its creation.
Another question follows: how does a method know which object it works on? Inside every method, the first parameter receives the instance the call was made on, and Python passes it automatically. That is the whole job of self, a plain parameter name made universal by convention. Calling first.credit(50) is the same as writing Account.credit(first, 50), a form that makes visible what the first one hides.
Checking that an object is the expected instance
At the borders of a program, objects arrive whose origin is unknown, and their nature sometimes has to be checked. The isinstance function answers that question and accepts descendants: an object built from a child class passes the test of the parent class, where a comparison on the type turns it down.
The difference matters as soon as inheritance comes into play, on pain of rejecting valid objects. The example runs both checks on a savings account, a kind of account.
class Savings(Account):
pass
savings = Savings("Camille")
isinstance(savings, Account) # True: a savings account is an account
type(savings) is Account # False: its exact type stays SavingsBoth answers are correct: isinstance asks whether the object can behave like an account, type which exact class it came from. In practice, the first question nearly always suffices.
That said, Python culture does not encourage such checks. duck typing prefers calling the expected method and catching the AttributeError if it is missing, which keeps the door open to objects of an unplanned class. The instance test keeps its place where data arrives with its shape guaranteed by nobody.
Frequently asked questions
What is the difference between a class and an instance?
The class is the blueprint, the instance is the object built from that blueprint. The blueprint never moves once written, whereas instances are created at will during execution, each with its own values and its own place in memory. A value that differs from one object to the next therefore belongs to the instance.
How many instances can be created from a single class?
As many as memory allows, and nothing limits them by default. A limit, if one is needed, is set by hand: a classmethod is precisely what counts those creations or frames them, for example to allow a single copy.
Why does printing an instance look like a memory address?
Because Python, given no instructions, settles for the class name and the location of the object: it has no idea which of your values deserve to be shown. Defining the magic method __repr__ replaces that line with whatever you find useful, the owner and the balance for instance. The Python course builds such classes step by step.