Seeing the following code:
class Super:
powers = 'no power'
def __init__(self, name):
self.name = name
def add_power(self, power):
self.powers = power
dog = Super('dog')
cat = Super('cat')
dog.add_power("bark")
print (dog.powers) # print bark
print (cat.powers) # print no power
it looks like python's class variable is independent to each instance because change dog instance's powers
variable from no power to bark does not affect the cat instance's powers
variable
However, by doing this:
class Super:
powers = ["no power"]
def __init__(self, name):
self.name = name
def add_power(self, power):
self.powers.append(power)
dog = Super('dog')
cat = Super('cat')
dog.add_power("bark")
print (dog.powers) # print ['no power', 'bark']
print (cat.powers) # print ['no power', 'bark'] # why cat.powers is also affected???
The example shows powers variable (it is a list this time) is static since append an element to dog instance's powers
also affects cat instance's powers
.
I also experimented with changing the powers to an int and increment powers by 1 by calling add_power, and they don't affect each other. So I am really confused why appending an element to a list which is a class variable affects other instances.
Class variables are also known as static variables, and they are declared outside a method, with the help of the keyword 'static'. Static variable is the one that is common to all the instances of the class. A single copy of the variable is shared among all objects.
Python has a static method that belongs to the class. It is just like a static variable that bounds to the class rather than the class's object. A static method can be called without creating an object for the class. It means we can directly call the static method with the reference of the class name.
Class variable − A variable that is shared by all instances of a class. Class variables are defined within a class but outside any of the class's methods. Class variables are not used as frequently as instance variables are.
Python doesn't have static variables but you can fake it by defining a callable class object and then using it as a function. Also see this answer. Note that __call__ makes an instance of a class (object) callable by its own name.
An instance variable name can shadow a class variable of the same name.
>>> class A:
... var = 'class var'
...
>>> a = A()
>>> vars(a) # no instance variables
{}
>>> a.var # attribute lookup resolved at the class level
'class var'
>>> a.var = 'instance var' # create an instance variable
>>> vars(a) # the name `var` now exists in the instance dict
{'var': 'instance var'}
>>> a.var # attribute lookup resolved at the instance level
'instance var'
>>> type(a).var # note: the class variable still exists!
'class var'
>>> del a.var # deletes from the instance dict
>>> a.var # ..but the name `var` remains available at the class level
'class var'
>>> vars(a) # instance dict is again empty
{}
It's not the case that the class variable is "static" (A.var
can be modified or deleted via normal attribute access). What's happening instead is: accessing a.var
first tries the name var
in the instance's namespace (a.__dict__
), and if that fails then the lookup falls back to checking in the class's namespace (A.__dict__
).
The reason you don't see the same behaviour when using a list on the class object is that this line is not an assignment statement:
self.powers.append(power)
A seemingly equivalent version which is using an assignment statement would recreate the same name shadowing that you observed:
self.powers = self.powers + [power] # not actually equivalent!
In summary: when using an integer or a string for the class attribute you were creating an entry in the instance namespace (because of the assignment statement), but when using a list you act directly on the class variable, which is indeed shared between all instances.
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