首页 > 解决方案 > 静态与类方法的类变量范围

问题描述

我在 python 类变量上发现了一个奇怪的行为(至少对我来说很奇怪)。

class Base(object):
    _var = 0

    @classmethod
    def inc_class(cls):
        cls._var += 1

    @staticmethod
    def inc_static():
        Base._var += 1

class A(Base):
    pass

class B(Base):
    pass

a = A()
b = B()

a.inc_class()
b.inc_class()
a.inc_static()
b.inc_static()

print(a._var)
print(b._var)
print(Base._var)

输出是1 1 2

这让我感到惊讶(我期待4 4 4),我想知道为什么?

标签: pythonstatic-methodsclass-methodclass-variables

解决方案


当用@classmethod第一个参数装饰clsinc_class(cls),就是类。<class '__main__.A'><class '__main__.B'>分别为AB。socls._var指的是A's _var,同样是 for B。在inc_static, 装饰@staticmethod没有参数,你明确指的<class '__main__.Base'>是不同的_var.

注意's 和's'_var': 0中的属性。正在做你期望它做的事情,将成员绑定到类,在这种情况下和.BaseA__dict__@classmethodAB

>>> Base.__dict__
mappingproxy({'__module__': '__main__', '_var': 0, 'inc_class': <classmethod 
object at 0x7f23037a8b38>, 'inc_static': <staticmethod object at 
0x7f23037a8c18>, '__dict__': <attribute '__dict__' of 'Base' objects>, 
'__weakref__': <attribute '__weakref__' of 'Base' objects>, '__doc__': None})

>>> A.__dict__
mappingproxy({'__module__': '__main__', '__doc__': None})`

调用后Base.inc_static()

>>> Base.__dict__
mappingproxy({'__module__': '__main__', '_var': 1, 'inc_class': 
<classmethod object at 0x7f23037a8b38>, 'inc_static': <staticmethod 
object at 0x7f23037a8c18>, '__dict__': <attribute '__dict__' of 'Base' 
objects>, '__weakref__': <attribute '__weakref__' of 'Base' objects>, 
'__doc__': None})

>>> A.__dict__
mappingproxy({'__module__': '__main__', '__doc__': None})

调用后A.inc_class()

>>> Base.__dict__
mappingproxy({'__module__': '__main__', '_var': 1, 'inc_class': 
<classmethod object at 0x7f23037a8b38>, 'inc_static': <staticmethod 
object at 0x7f23037a8c18>, '__dict__': <attribute '__dict__' of 'Base' 
objects>, '__weakref__': <attribute '__weakref__' of 'Base' objects>, 
'__doc__': None})

>>> A.__dict__
mappingproxy({'__module__': '__main__', '__doc__': None, '_var': 1})

有趣的是A's_var是如何初始化的。请注意,您cls._var += 1之前所做cls._var的已经定义。正如这里所解释的,cls._var += 1相当于cls._var = cls._var; cls._var += 1. 由于python 查找的方式,第一次读取cls._var将失败A并继续在Base. 在赋值_var被添加到A's__dict__的值Base._var,然后一切都很好。

>>> class Base(object):
...     _var = 10
...     @classmethod
...     def inc_class(cls):
...         cls._var += 1
... 
>>> class A(Base):
...     pass
... 
>>> A.__dict__
mappingproxy({'__module__': '__main__', '__doc__': None})
>>> A.inc_class()
>>> A.__dict__
mappingproxy({'__module__': '__main__', '__doc__': None, '_var': 11})

推荐阅读