为什么在Python中修改类属性不会修改对象属性?

时间:2019-10-27 17:30:54

标签: python class

因此,如下面的代码片段所示,我无法完全理解Python如何访问类变量。

class Test():
    var = 1

obj = Test()
obj2 = Test()
print(Test.var, '\t',obj.var, '\t', obj2.var)

obj.var += 1
print(Test.var, '\t',obj.var, '\t', obj2.var)

Test.var += 5
print(Test.var, '\t',obj.var, '\t', obj2.var)

给出以下输出:

1        1       1  # Makes sense
1        2       1  # Also makes sense
6        2       6  # I would expect obj.var to become '7'

obj.var保持不变。这是否意味着修改obj.var创建了一个对象特定的变量,该变量现在独立于类属性var

1 个答案:

答案 0 :(得分:1)

混乱可能在obj.var += 1指令中。

这等效于obj.var = obj.var + 1。右侧在对象上找不到var,因此委托给该类。但是,左侧将结果存储在对象上。从那时起,在对象上查找var将不再委托给class属性。

>>> class Test():
>>>    var = 1
>>> Test.__dict__
mappingproxy({'__module__': '__main__',
              'var': 1,
              '__dict__': <attribute '__dict__' of 'Test' objects>,
              '__weakref__': <attribute '__weakref__' of 'Test' objects>,
              '__doc__': None})
>>> obj = Test()
>>> obj.__dict__
{}
>>> obj.var += 1
>>> obj.__dict__
{'var': 2}

如果删除对象属性,则再次暴露类属性:

>>> Test.var += 5
>>> obj.var
2
>>> del obj.var
>>> obj.var
6

the docs中的相关位:

  

一个类实例具有一个实现为字典的名称空间,这是搜索属性引用的第一位。如果在该位置找不到属性,并且实例的类具有该名称的属性,则搜索将继续使用该类的属性。


关于您的后续问题,这是使用data descriptors进行操作的一种方法,尽管我认为这不是Python语言。特别是,在类上具有数据描述符是不寻常的(如果不小心,可能会破坏事情)。

class VarDescriptorMetaclass(type):
    """Metaclass to allow data descriptor setters to work on types."""
    def __setattr__(self, name, value):
        setter = getattr(self.__dict__.get(name), '__set__')
        return setter(None, value) if setter else super().__setattr__(name, value)

class VarDescriptor(object):
    """Data descriptor class to support updating property via both class and instance."""
    def __init__(self, initial_value):
        self.value = initial_value
    def __get__(self, obj, objtype):
        if obj and hasattr(obj, '_value'):
            return self.value + obj._value
        return self.value
    def __set__(self, obj, value):
        if obj:
            obj._value = value - self.value
        else:
            self.value = value
    def __delete__(self, obj):
        if obj and hasattr(obj, '_value'):
            del obj._value

class Test(metaclass=VarDescriptorMetaclass):
    var = VarDescriptor(initial_value=1)

这似乎可以满足您的要求:

>>> obj = Test()
>>> obj.var
1
>>> obj.var += 1
>>> obj.var
2
>>> Test.var += 5
>>> Test.var
6
>>> obj.var
7