python中的cls vs.self vs.class调用

时间:2018-07-14 00:33:14

标签: python class object self

我是Python的初学者,并且使用Lutz的书来理解classmethodstaticmethodinstancemethod。该代码的目的是通过计算创建的实例数来了解clsself和直接类调用(Spam1.numInstances)之间的区别。

这是从书中得出的一个例子。我不确定为什么通过子类Spam1numInstances调用时,父类(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.numInstancesdef没有增加?

这是我尝试回答的问题:

a.i)据我了解,cls用于访问类属性。对于deself.numInstances用于访问实例属性,该属性为零,因为Sub1将{{1}的继承属性numInstances的值归零。 }。 Spam1访问cls的类属性,该属性也与Sub1类的属性相同。因此,我们在输出中看到的Sub1self值分别是cls实例和类。我的理解正确吗?

a.ii)Sub1f继承numInstances。因此,Spam1中的self.numInstancescls.numInstancesf中取值。它们的值是递增的,但不是Spam1的值,因为Spam1引用了cls,并且因为Other1引用了selffOther1的对象。因此,永远不会碰到Spam1的{​​{1}}。

b)我对numInstancesself.numInstancescls.numInstances之间的区别的理解正确吗?如果没有,可以请人解释吗?

我相信我的问题很基本。我希望有人能帮助我。我迷路了。

2 个答案:

答案 0 :(得分:1)

使用Sub1的实例时,无法访问Spam1的{​​{1}}属性(除非通过显式写入numInstances); Spam1.numInstances中的cls引用了count(),并且在该类中找到了该属性,因此无需进一步查找继承链。

在使用Sub1的实例时,Other1的初始读取确实来自numInstances-但是,一旦您分配了值,它就会进入{{1} }(因为Spam1现在是Other1),并且所有对该名称的进一步引用现在都找到了,而不是cls的版本。

在您的代码中有三个不同的名为Other1的类属性:两个在定义类Spam1numInstances之后就存在,一个存在于第一个实例之后。 Spam1已创建。

答案 1 :(得分:1)

您在这里有一些误解:

  1. 此代码中的任何地方都从不有一个名为numInstances的实例属性。 self.numInstances 检查一个实例属性,但是由于没有{em>分配给self.numInstances,因此没有要读取的实例属性,因此可以访问{{1 }}最终读取了class属性。
  2. self.numInstances并不完全“继承”父类的值。执行f时,它将尝试查找cls.numInstances += 1,发现它不存在,并检查超类,最终找到Other1.numInstances。它将递增该值,然后将其分配回Spam1.numInstances(即使在适当的位置工作,Python中的Other1.numInstances也会始终重新分配;对于不可变的+=,工作是 not < / em>);将来,对int的访问将不会检查Other1.numInstances,因为Spam1.numInstances的属性现在存在。