【问题标题】:cls vs. self vs. Class call in pythoncls vs. self vs. python中的类调用
【发布时间】:2018-12-22 08:58:28
【问题描述】:

我是 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) 为什么在创建d、e 和f 时Spam1.numInstances 没有增加?

这是我试图回答这个问题的尝试:

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

a.ii) f 从 Spam1 继承 numInstances。因此,f 的 self.numInstances 和 cls.numInstances 采用来自 Spam1 的值。它们的值是递增的,但不是 Spam1,因为 cls 引用 Other1 并且因为 self 引用 f,这是 Other1 的对象。因此,Spam1 的 numInstances 永远不会被触及。

b) 我对self.numInstances、cls.numInstances 和Spam1.numInstances 之间差异的理解是否正确?如果不是,有人可以解释一下吗?

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

【问题讨论】:

  • 重要的是要了解self 和cls 在这里只是常规名称。他们可能是foo 或banana。它们的特别之处在于它们是第一个参数,因此当在实例上调用时它们会隐式传递给实例(以及当您使用 classmethod 装饰器时的类)

标签: python class object self


【解决方案1】:

这里有几个误解:

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

【讨论】:

    【解决方案2】:

    当您使用Sub1 的实例时,Spam1 的numInstances 属性不可访问(除非通过显式写入Spam1.numInstances); count()中的cls指的是Sub1,属性在那个类中,所以不需要再往继承链上找。

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

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

    【讨论】:

      猜你喜欢
      • 2019-02-17
      • 1970-01-01
      • 2011-06-04
      • 1970-01-01
      • 1970-01-01
      • 2011-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多