我在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
),我想知道为什么吗?
答案 0 :(得分:2)
用@classmethod
装饰时,cls
的第一个参数inc_class(cls)
是该类。 <class '__main__.A'>
和<class '__main__.B'>
分别为A
和B
。因此,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类,但它们是完全不同的对象。通过a
和b
的实例化,您有两个对象,它们属于两个单独的类。通话时
a.inc_class()
b.inc_class()
您一次增加了类A的_var
属性,然后对类B进行了相同的操作。即使它们共享相同的名称,它们也是不同的对象。如果您有第二个类A的实例,例如a2
,并且您将再次调用该函数,则这两个调用将操纵相同的变量。这说明了如何获得前两个输出。
第三个输出引用基类对象。同样,即使名称相同,它也是一个不同的对象。您将第三个对象增加两次,因此得到2
作为答案。