简体   繁体   中英

Is Python class variable static?

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM