关于python:用self调用类变量

Calling class variable with self

我怎么能想出这个有趣(至少对我来说)的例子呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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()

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

1
2
3
4
5
6
7
$ python test.py
<__main__.Something object at 0x7f03a80e0518>
1
<__main__.Something object at 0x7f03a80cfcc0>
1
<__main__.Something object at 0x7f03a80cfcf8>
1

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

如果我使用@classmethod装饰器,我就能够获得我所期望的行为。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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()

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

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

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

这是我用错方法使用的类变量吗?我知道cls的名称,如果是约定的话,那么我可能真的在访问一个类变量,但是我不能更改它的值,因为我没有用@classmethod修饰器修饰这个方法。

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


所发生的是,self.a指的是两个不同时期的事物。

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

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

(一个小问题,与问题无关:您不应该直接访问双下划线方法。不呼叫something2.__str__(),呼叫str(something2)等)


丹尼尔·罗斯曼的回答清楚地解释了这个问题。以下是一些附加的要点,希望能有所帮助。您可以使用type(self).a而不是self.a。还可以查看讨论python:self-vs-type(self)和类变量的正确使用python:self.u类与类型(self)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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()