使用 self 调用 class 变量

Calling class variable with self

你怎么想出这个有趣的(至少对我来说)例子。

import numpy as np


class Something(object):
    a = np.random.randint(low=0, high=10)

    def do(self):
        self.a += 1
        print(self.a)

if __name__ == '__main__':
    something = Something()
    print(something.__str__())
    something.do()
    something2 = Something()
    print(something2.__str__())
    something2.do()
    something3 = Something()
    print(something3.__str__())
    something3.do()

以上在控制台中打印以下内容:

$ python test.py
<__main__.Something object at 0x7f03a80e0518>
1
<__main__.Something object at 0x7f03a80cfcc0>
1
<__main__.Something object at 0x7f03a80cfcf8>
1

我有点困惑,因为我(错误地)假设 a 的值会增加。

如果我使用 @classmethod 装饰器,我可以获得预期的行为。

import numpy as np


class Something(object):
    a = np.random.randint(low=0, high=10)

    @classmethod
    def do(cls):
        cls.a += 1
        print(cls.a)

if __name__ == '__main__':
    something = Something()
    print(something.__str__())
    something.do()
    something2 = Something()
    print(something2.__str__())
    something2.do()
    something3 = Something()
    print(something3.__str__())
    something3.do()

这会在控制台中正确打印以下内容。

python test.py
<__main__.Something object at 0x7faac77becc0>
3
<__main__.Something object at 0x7faac77becf8>
4
<__main__.Something object at 0x7faac77c3978>
5

现在,我想知道在第一个示例中,当我调用 self.a 时,我正在访问什么?它不是 class 变量,因为我似乎无法更改它的值。它也不是实例变量,因为它似乎在同一 class 的不同对象之间共享。你会怎么称呼它?

这是我使用错误的 class 变量吗?我知道 cls 名称如果约定,所以也许我真的在访问 class 变量,但我无法更改它的值,因为我没有用 [= 修饰方法16=]装饰器。

这是对语言的一种非法使用吗?我的意思是最好不要这样做以避免在后期引入错误?

发生的事情是 self.a 在不同的时间指的是 两个 事物。

当名称不存在实例变量时,Python 将查找 class 上的值。因此 self.a 检索到的值将是 class 变量。

但是当通过self设置一个属性时,Python将总是设置一个实例变量。所以现在 self.a 是一个新的实例变量,它的值等于 class 变量 + 1。这个属性隐藏了 class 属性,你不能再通过 self 访问它但只能通过 class.

(一个小问题,与问题无关:你永远不应该直接访问 double-underscore 方法。不要调用 something2.__str__(),而是调用 str(something2) 等)

Daniel Roseman 的回答清楚地说明了问题。下面补充几点,希望对大家有所帮助。 您可以使用 type(self).a 代替 self.a。也看看讨论 Python: self vs type(self) and the proper use of class variablesPython: self.__class__ vs. type(self)

import numpy as np


class Something(object):
    a = np.random.randint(low=0, high=10)

    def do(self):
        type(self).a += 1
        print(type(self).a)

if __name__ == '__main__':
    something = Something()
    print(str(something ))
    something.do()
    something2 = Something()
    print(str(something2))
    something2.do()
    something3 = Something()
    print(str(something3))
    something3.do()