静态与类方法的类变量范围

时间:2019-01-14 22:48:28

标签: python static-methods class-method class-variables

我在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),我想知道为什么吗?

2 个答案:

答案 0 :(得分:2)

@classmethod装饰时,cls的第一个参数inc_class(cls)是该类。 <class '__main__.A'><class '__main__.B'>分别为AB。因此,cls._var是指A的{​​{1}},对于_var也是类似的。在用B修饰的inc_static中没有参数,您显式地指的是@staticmethod,即另一个<class '__main__.Base'>

请注意_var'_var': 0的{​​{1}}中的Base属性。 A正在执行您期望的操作,将成员绑定到类,在本例中为__dict__@classmethod

A

致电B后:

>>> 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()的{​​{1}}是如何初始化的。请注意,您必须在定义>>> 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。如here所述,_var等效于cls._var += 1。由于way python does lookup,对cls._var的第一次读取将在cls._var += 1中失败,并继续在cls._var = cls._var; cls._var += 1中找到它。在分配时,将cls._var添加到A的{​​{1}}中,并将其值设为Base,然后一切正常。

_var

答案 1 :(得分:1)

尽管这两个类都继承自Base类,但它们是完全不同的对象。通过ab的实例化,您有两个对象,它们属于两个单独的类。通话时

a.inc_class()
b.inc_class()

您一次增加了类A的_var属性,然后对类B进行了相同的操作。即使它们共享相同的名称,它们也是不同的对象。如果您有第二个类A的实例,例如a2,并且您将再次调用该函数,则这两个调用将操纵相同的变量。这说明了如何获得前两个输出。

第三个输出引用基类对象。同样,即使名称相同,它也是一个不同的对象。您将第三个对象增加两次,因此得到2作为答案。