(especially in terms of their behavior under "normal use", internally I suppose they most likely are implemented differently)
Take these two classes as an example:
class A:
d = 4
class A:
def __init__(self, d=4):
self.d = d
No matter what version you choose, when you run the code below, you'll get the same result:
a2 = A()
a = A()
print(a.d) # 4
a.d = 2
print(a.d) # 2
print(a2.d) # 4
I came to think of this question after reading:
Are there any differences between a class variable and an instance variable with a default value?
Well, yes obviously: a class attribute (not "variable") belongs to the class, an instance attribute belongs to the instance.
In what context should I use which version?
Use class attributes when you want the attribute to be shared by all instances of the class, and instance attributes when you want the attribute to be specific to this exact instance. In practice, you'll seldom have a need for class attributes.
Note that if you define the same attribute for both the class and the instance, the one on the instance will shadow the class's one.
nb: the above is a very very crude simplification, but else I'd need to explain the whole Python object model, and this deserves a full book
Take these two classes as an example (...) no matter what version you choose, when you run the code below, you'll get the same result
Yes, that's to be expected for this code snippet.
For a:
In the first case, when you first print a.d, a doesn't have an instance attribute d so you're getting the class attribute value. Then you create the instance attribute a.d by assigning to it, and from then on it shadows the class attribute.
In the second case, a.d initially has it's default value, then you rebind it to another value... quite ordinary stuff.
For a2:
In the first case, a2.a will always be 4 because you haven't shadowed it with an instance attribute, so it's getting the value from the class.
In the second case it will always be 4 because you didn't rebind the instance attribute so it's still the default value.
Now try the same thing with a list as attribute, and appending to the list instead of rebinding it:
class A:
d = []
class B:
def __init__(self):
self.d = []
def test(cls):
print("test {}".format(cls.__name__))
a = cls()
print(a.d)
a.d.append(2)
print(a.d)
a2 = cls()
print(a2.d)
if __name__ == "__main__":
test(A)
test(B)
As a last note: you may have seen (or you may one day see) code using class attributes as default values for instances - or you may be tempted in doing so yourself (hence the mention of a 'default' value for the instance attribute) -, as in your first example. This is bad practice. It's confusing at best, and can lead to wrong behaviour if the attribute is of a mutable type.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With