@staticmethod 和@classmethod 如何作用于python 中的变量?

How does @staticmethod and @classmethod act on the variable in python?

我写了一个简单的程序。

class Sample:

    num =45

    def __init__(self):
        print('Inside constructor')

    @classmethod
    def modifyUsingClassMethod(cls):
        cls.num = cls.num + 45

    @staticmethod
    def modifyUsingStaticMethod():
        Sample.num = Sample.num+5

s1 = Sample()
s2 = Sample()

s1.modifyUsingClassMethod()
print(s1.num, s2.num)

s1.num = s1.num + 5
print(s1.num)

s1.modifyUsingClassMethod()
print(s1.num, s2.num)

s1.modifyUsingStaticMethod()
print(s1.num, s2.num)

输出:

Inside constructor
Inside constructor
90 90
95
95 135
95 140

谁能解释一下 @staticmethod@classmethod 如何以及为什么作用于变量 'num'?。为什么即使在我使用 modifyUsingClassMethod() 使用 s1 实例更改了 num 的值后,输出仍显示 95,135,为什么它在两种情况下都不使用 @staticmethod@classmethod 进行更新?

我想当我使用 class 对象引用变量 num 时,python 将变量 num 视为实例变量,但是当我更改变量 num 使用 Class 名称,那么值不会在 s1 中更新,而是在 s2 中更新。我很困惑 @classmethod@staticmethod 是如何工作的。

操作:

s1.num = s1.num + 5

导致 s1 包含 .num 的本地副本。如果您从测试代码中删除它,您会发现如果没有它,属性会随着时间的推移继续自我跟踪。

您的 class-method 和 static-method 都只会更改 class-level 变量。问题是你在实例变量 s1 隐藏了 你的 class-variable num,当你这样做时:

s1.num = s1.num + 5

这创建了一个实例变量,它隐藏实例命名空间中的 class 变量。当你访问一个对象时,将检查实例的命名空间,如果没有找到具有该名称的属性,它将尝试classes name-space,然后它会检查所有的命名空间classes in the method-resultion-order: MRO(这个继承)。

考虑一下你的例子:

In [1]: class Sample:
   ...:     num =45
   ...:
   ...:     def __init__(self):
   ...:         print('Inside constructor')
   ...:
   ...:     @classmethod
   ...:     def modifyUsingClassMethod(cls):
   ...:         cls.num = cls.num + 45
   ...:
   ...:     @staticmethod
   ...:     def modifyUsingStaticMethod():
   ...:         Sample.num = Sample.num+5
   ...:

In [2]: s1 = Sample()
   ...: s2 = Sample()
   ...:
   ...: s1.modifyUsingClassMethod()
   ...: print(s1.num,s2.num)
   ...:
   ...: s1.num = s1.num + 5
   ...: print(s1.num)
   ...:
   ...: s1.modifyUsingClassMethod()
   ...: print(s1.num,s2.num)
   ...:
   ...: s1.modifyUsingStaticMethod()
   ...: print(s1.num,s2.num)
   ...:
Inside constructor
Inside constructor
90 90
95
95 135
95 140

现在看看对象:

In [4]: vars(Sample)
Out[4]:
mappingproxy({'__dict__': <attribute '__dict__' of 'Sample' objects>,
              '__doc__': None,
              '__init__': <function __main__.Sample.__init__>,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'Sample' objects>,
              'modifyUsingClassMethod': <classmethod at 0x107c3fe48>,
              'modifyUsingStaticMethod': <staticmethod at 0x107c3ff98>,
              'num': 140})

In [5]: vars(s1)
Out[5]: {'num': 95}

In [6]: vars(s2)
Out[6]: {}

你可以清楚地看到 s1 的命名空间中有 num,隐藏了 Sample 的命名空间。

注意当我们从实例 name-space:

中删除 num 时会发生什么
In [11]: del s1.num

In [12]: s1.num
Out[12]: 140