There is a significant semantic difference (beyond performance considerations):
- when the attribute is defined on the instance (which is what we usually do), there can be multiple objects referred to. Each gets a totally separate version of that attribute.
- when the attribute is defined on the class, there is only one underlying object referred to, so if operations on different instances of that class both attempt to set/(append/extend/insert/etc.) the attribute, then:
- if the attribute is a builtin type (like int, float, boolean, string), operations on one object will overwrite (clobber) the value
- if the attribute is a mutable type (like a list or a dict), we will get unwanted leakage.
>>> class A: foo =  >>> a, b = A(), A() >>> a.foo.append(5) >>> b.foo  >>> class A: ... def __init__(self): self.foo =  >>> a, b = A(), A() >>> a.foo.append(5) >>> b.foo