首页 > 解决方案 > 为什么修改类属性不会修改 Python 中的对象属性?

问题描述

因此,如下面的代码片段所示,我无法完全理解 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

标签: pythonclass

解决方案


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

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

>>> 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

文档中的相关位:

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


关于您的后续问题,这是使用数据描述符做您想做的事情的一种方法,尽管我认为它不是非常 Pythonic。特别是,在类上有数据描述符是不寻常的(如果你不小心可能会破坏事情)。

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

推荐阅读