cls vs. self与python中的类调用

cls vs. self vs. Class call in python

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

这是从这本书中衍生出来的一个例子。我不确定为什么父类(Spam1属性(numInstances属性)在通过Sub1Other1调用时没有增加,后者是子类。

这是我的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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的"无引用"。由于感到沮丧,我读了一些这样的线程:cls()函数在类方法中做什么?,python类中使用的"cls"变量是什么?还有python-self,没有self和cls,但是我不能理解发生了什么。

具体来说,我的问题是:

a)创建def时,为什么Spam1.numInstances不增加?

我试图回答这个问题:

a.i)据我所知,cls用于访问类属性。对于de来说,self.numInstances用于访问实例属性,该属性为零,因为Sub1使Spam1继承属性numInstances的值为零。cls访问Sub1类属性,与Sub1类属性相同。因此,我们在输出中看到的selfcls值分别是Sub1实例和类的值。我的理解正确吗?

a.ii)f继承numInstancesSpam1继承。因此,fself.numInstancescls.numInstancesSpam1的值。由于clsOther1selffOther1的对象,因此它们的值是递增的,而不是Spam1的值。因此,Spam1numInstances从未被触碰过。

b)我对self.numInstancescls.numInstancesSpam1.numInstances之间的差异的理解是否正确?如果没有,有人能解释一下吗?

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


你有几个误解:

  • 在这段代码中的任何时候都没有名为numInstances的实例属性。self.numInstances检查实例属性,但由于没有为self.numInstances赋值,因此没有要读取的实例属性,因此访问self.numInstances最终会读取类属性。
  • f并不完全"继承"父类的值。当执行cls.numInstances += 1时,它试图查找Other1.numInstances,发现它不存在,并检查超类,最终找到Spam1.numInstances。它增加该值,然后重新分配给Other1.numInstances(python中的+=总是重新分配,即使它在适当的位置执行工作,并且对于不可变的int来说,工作不在适当的位置);将来,访问Other1.numInstances不会检查Spam1.numInstances,因为Other1的属性现在已经存在。

  • 当您使用Sub1实例时,Spam1numInstances属性是不可访问的(除了通过显式写入Spam1.numInstances之外);count()中的cls是指Sub1的,并且该属性在该类中被发现,因此不需要进一步查找继承链。

    当您处理Other1的实例时,numInstances的初始读数确实来自Spam1,但一旦您分配了一个值,该值就进入Other1(因为cls现在是Other1),并且所有对该名称的进一步引用现在都发现它不是Spam1的版本。

    代码中有三个不同的类属性,名为numInstances:定义了类Spam1Sub1后就存在两个,创建了Other1的第一个实例后就存在一个。