【问题标题】:Understanding the difference between `self`and `cls` and whether they refer to the same attributes了解 `self` 和 `cls` 之间的区别以及它们是否引用相同的属性
【发布时间】:2018-07-30 12:33:29
【问题描述】:

我试图了解self 和cls 之间是否存在差异,但我很苦恼,尽管存在很多关于这个主题的讨论。例如:

class maclass():
    A = "class method"

    def __init__(self):
        self.B = "instance method"

    def getA_s(self):
        print(self.A)

    def getA_c(cls):
        print(cls.A)

    def getB_s(self):
        print(self.B)

    def getB_c(cls):
        print(cls.B)

C = maclass()
C.getA_s()
C.getA_c()
C.getB_s()
C.getB_c()

给我:

class method
class method
instance method
instance method

所以无论我使用self 还是cls,它总是引用同一个变量。当我在Init__ 中添加self.A 时,cls.A 就被替换了

def __init__(self):
        self.B = "instance method"
        self.A = "new instance method"

我得到:

new instance method
new instance method
instance method
instance method

如果它们相同,我不明白有两种方法来调用类成员的意义?我知道这是这个论坛上的一个常见问题,但我真的不明白为什么我们会使用不同的词来指代同一事物(我们甚至可以使用任何变量名来代替 self 或 cls)。

更新

以下情况:

class maclass():
    A = "class method, "

    def __init__(self):
        self.A = "instance method, "

    def getA_s(self):
        print(self.A) #give me "instance method, "

    @classmethod
    def getA_c(cls):
        print(cls.A) #give me "class method, "

C = maclass()
C.getA_s()
C.getA_c()
print(' ')
print(C.A) #give me "instance method, "

我明白了:

instance method, 
class method, 

instance method,    

所以在这种情况下,maclass: cls.A 和 self.A 不引用同一个变量。

【问题讨论】:

  • 它们只是命名约定。您可以将第一个参数命名为任何您喜欢的名称。
  • 尝试调用它kevin,它仍然可以以相同的方式工作。我们使用self 和cis 作为可读性 的约定,仅此而已。

标签: python class self


【解决方案1】:

所有您的方法都是实例方法。它们都不是类方法。

方法的第一个参数命名为self 只是按照约定。您可以将其命名为任何您想要的名称,而将其命名为 cls 不会使其成为对该类的引用。第一个参数绑定到一个实例是由于方法查找的工作方式(访问C.getA_s 会产生一个绑定的方法对象,调用该对象会导致C 被传递到原始函数@ 987654327@),参数的名称不起作用。

在您的方法中,您只是在引用实例属性。 A 属性最终只在类上定义并不重要,您仍然通过C.A 访问该属性(其中C 是您创建的实例),而不是maclass.A。如果没有实例属性遮蔽它,则在实例上查找属性也会找到在类上定义的属性。

要使方法成为类方法,请用@classmethod decorator 装饰它:

@classmethod
def getA_c(cls):
    print(cls.A)

现在cls 将始终是对类的引用,而不是对实例的引用。我需要再次强调,我为第一个参数选择的名称对于 Python 实际上并不重要,但cls 是这里的约定,因为这样更容易提醒读者此方法绑定到类对象。

请注意,如果您对getB_c() 方法执行此操作,那么尝试在该方法中访问cls.B 将失败,因为maclass 类对象上没有B 属性。

这是因为 classmethod 将函数包装在 descriptor object 中,从而覆盖了正常的函数绑定行为。正是描述符 protocol 导致方法在作为实例上的属性访问时绑定到实例,classmethod 对象重定向该绑定过程。

这是一个内联 cmets 的简短演示,我使用 Python 转换来命名类(使用 CamelCase),以及用于实例、属性、函数和方法(使用 snake_case):

>>> class MyClass():
...     class_attribute = "String attribute on the class"
...     def __init__(self):
...         self.instance_attribute = "String attribute on the instance"
...     @classmethod
...     def get_class_attribute(cls):
...         return cls.class_attribute
...     def get_instance_attribute(self):
...         return self.instance_attribute
...     @classmethod
...     def get_instance_attribute_on_class(cls):
...         return cls.instance_attribute
...
>>> instance = MyClass()
>>> instance.class_attribute  # class attributes are visible on the instance
'String attribute on the class'
>>> MyClass.class_attribute   # class attributes are also visible on the class
'String attribute on the class'
>>> instance.get_class_attribute()  # bound to the class, but that doesn't matter here
'String attribute on the class'
>>> instance.class_attribute = "String attribute value overriding the class attribute"
>>> instance.get_class_attribute()  # bound to the class, so the class attribute is found
'String attribute on the class'
>>> MyClass.get_instance_attribute_on_class()   # fails, there is instance_attribute on the class
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 12, in get_instance_attribute_on_class
AttributeError: type object 'MyClass' has no attribute 'instance_attribute'

请注意,即使我们在实例上设置了同名的属性,类方法也会访问类属性。

接下来是绑定行为:

>>> MyClass.get_instance_attribute   # accessing the method on the class gives you the function
<function MyClass.get_instance_attribute at 0x10f94f268>
>>> instance.get_instance_attribute  # accessing the method on the instance gives you the bound method
<bound method MyClass.get_instance_attribute of <__main__.MyClass object at 0x10f92b5f8>>
>>> MyClass.get_class_attribute      # class methods are always bound, to the class
<bound method MyClass.get_class_attribute of <class '__main__.MyClass'>>
>>> instance.get_class_attribute     # class methods are always bound, to the class
<bound method MyClass.get_class_attribute of <class '__main__.MyClass'>>

绑定的方法告诉你它们被绑定到什么到,调用方法传入绑定的对象作为第一个参数。也可以通过查看绑定方法的 __self__ 属性来内省该对象:

>>> instance.get_instance_attribute.__self__  # the instance
<__main__.MyClass object at 0x10f92b5f8>
>>> instance.get_class_attribute.__self__     # the class
<class '__main__.MyClass'>

【讨论】:

  • 因此在您的示例中,cls.A 与 self.A 不同。如果我从maclass 实例中执行C.A,我将从maclass 中获得self.A,对吗?
  • @ymmx:我不确定你在问什么。 cls 和 self 是什么?您的示例代码中的参数名称?这些始终是同一个对象,即您分配给C 的maclass 的实例。
  • @ymmx:如果您将@classmethod 添加到您的get*_c 定义中,那么cls 将绑定到maclass 类,因此cls.A 只会找到类属性.调用C.getB_c() 会失败,因为maclass 类对象上没有B 属性。
  • 好的,我明白了。所以self和cls如果至少使用了一个类方法装饰器是不同的。
  • 我发现这个答案非常彻底和清晰。谢谢!
猜你喜欢
  • 2011-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-04
  • 2012-09-25
  • 1970-01-01
相关资源
最近更新 更多