我是Python的初学者,并且使用Lutz的书来理解classmethod
,staticmethod
和instancemethod
。该代码的目的是通过计算创建的实例数来了解cls
,self
和直接类调用(Spam1.numInstances
)之间的区别。
这是从书中得出的一个例子。我不确定为什么通过子类Spam1
和numInstances
调用时,父类(Sub1
)属性(Other1
)不会增加。
这是我的代码:
class Spam1:
numInstances = 0
def count(cls):
cls.numInstances += 1
print("In count -> number of instances: cls, Spam", cls.numInstances, Spam1.numInstances)
def __init__(self):
print("-----")
print("In init, before -> number of instances: self, Spam",self.numInstances,Spam1.numInstances )
self.count()
print("In init, after -> number of instances: self, Spam",self.numInstances,Spam1.numInstances )
print("-----")
count=classmethod(count)
class Sub1(Spam1):
numInstances = 0
class Other1(Spam1):
pass
a=Spam1() #Output after increment: 1,1,1 (self, cls, Spam1)
b=Spam1() #Output after increment: 2,2,2 (self, cls, Spam1)
c=Spam1() #Output after increment: 3,3,3 (self, cls, Spam1)
d=Sub1() #Output after increment: 1,1,3 (self, cls, Spam1)
e=Sub1() #Output after increment: 2,2,3 (self, cls, Spam1)
f=Other1() #Output after increment: 4,4,3 (self, cls, Spam1)
我花了一天的时间尝试调试此代码,但是我无法理解cls.numInstances
的工作原理,因为PyCharm在调试模式下将显示cls.numInstances
的“无参考”。沮丧的是,我读了几个SO线程:What does cls() function do inside a class method?,What is the 'cls' variable used for in Python classes?和Python - self, no self and cls,但是我不知道发生了什么。
具体来说,这是我的问题:
a)为什么创建Spam1.numInstances
,d
和e
时f
没有增加?
这是我尝试回答的问题:
a.i)据我了解,cls
用于访问类属性。对于d
和e
,self.numInstances
用于访问实例属性,该属性为零,因为Sub1
将{{1}的继承属性numInstances
的值归零。 }。 Spam1
访问cls
的类属性,该属性也与Sub1
类的属性相同。因此,我们在输出中看到的Sub1
和self
值分别是cls
实例和类。我的理解正确吗?
a.ii)Sub1
从f
继承numInstances
。因此,Spam1
中的self.numInstances
和cls.numInstances
从f
中取值。它们的值是递增的,但不是Spam1
的值,因为Spam1
引用了cls
,并且因为Other1
引用了self
,f
是Other1
的对象。因此,永远不会碰到Spam1
的{{1}}。
b)我对numInstances
,self.numInstances
和cls.numInstances
之间的区别的理解正确吗?如果没有,可以请人解释吗?
我相信我的问题很基本。我希望有人能帮助我。我迷路了。
答案 0 :(得分:1)
使用Sub1
的实例时,无法访问Spam1
的{{1}}属性(除非通过显式写入numInstances
); Spam1.numInstances
中的cls
引用了count()
,并且在该类中找到了该属性,因此无需进一步查找继承链。
在使用Sub1
的实例时,Other1
的初始读取确实来自numInstances
-但是,一旦您分配了值,它就会进入{{1} }(因为Spam1
现在是Other1
),并且所有对该名称的进一步引用现在都找到了,而不是cls
的版本。
在您的代码中有三个不同的名为Other1
的类属性:两个在定义类Spam1
和numInstances
之后就存在,一个存在于第一个实例之后。 Spam1
已创建。
答案 1 :(得分:1)
您在这里有一些误解:
numInstances
的实例属性。 self.numInstances
检查一个实例属性,但是由于没有{em>分配给self.numInstances
,因此没有要读取的实例属性,因此可以访问{{1 }}最终读取了class属性。self.numInstances
并不完全“继承”父类的值。执行f
时,它将尝试查找cls.numInstances += 1
,发现它不存在,并检查超类,最终找到Other1.numInstances
。它将递增该值,然后将其分配回Spam1.numInstances
(即使在适当的位置工作,Python中的Other1.numInstances
也会始终重新分配;对于不可变的+=
,工作是 not < / em>);将来,对int
的访问将不会检查Other1.numInstances
,因为Spam1.numInstances
的属性现在存在。