理解`self`和`cls`之间的区别,因为它们指的是相同的属性

ymm*_*mmx 6 python class self

我试图理解他们之间的区别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()
Run Code Online (Sandbox Code Playgroud)

这给了我:

class method
class method
instance method
instance method
Run Code Online (Sandbox Code Playgroud)

所以无论我使用什么selfcls它离开都是指相同的变量.当我加入 self.AInit__,该cls.A只是更换

def __init__(self):
        self.B = "instance method"
        self.A = "new instance method"
Run Code Online (Sandbox Code Playgroud)

我得到:

new instance method
new instance method
instance method
instance method
Run Code Online (Sandbox Code Playgroud)

如果它们是相同的话,我不明白有两种方法可以调用类成员吗?我知道这是这个论坛上的一个常见问题,但我真的不明白为什么我们可以用不同的词来指代同一个东西(我们甚至可以使用任何单词而不是selfcls)

更新

在以下情况中:

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, "
Run Code Online (Sandbox Code Playgroud)

我明白了:

instance method, 
class method, 

instance method,    
Run Code Online (Sandbox Code Playgroud)

所以在这种情况下,在maclass cls.Aself.A不引用相同的变量.

Mar*_*ers 20

你的所有方法都是实例方法.它们都不是类方法.

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

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

要使方法成为类方法,请使用@classmethod装饰器进行装饰:

@classmethod
def getA_c(cls):
    print(cls.A)
Run Code Online (Sandbox Code Playgroud)

现在cls将始终是对类的引用,从不对实例.我需要再次强调,Python为我的第一个参数选择了什么名称并不重要,但是cls这里的约定使得更容易提醒读者这个方法绑定到类对象.

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

那是因为classmethod将函数包装在描述符对象中,该对象覆盖了正常的函数绑定行为.描述符协议使得方法在作为实例上的属性访问时绑定到实例,classmethod对象重定向该绑定过程.

这是一个带内联注释的简短演示,我使用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'
Run Code Online (Sandbox Code Playgroud)

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

接下来是绑定行为:

>>> 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'>>
Run Code Online (Sandbox Code Playgroud)

绑定方法告诉您它们绑定的内容,调用该方法将该绑定对象作为第一个参数传递.通过查看__self__绑定方法的属性,也可以对该对象进行内省:

>>> instance.get_instance_attribute.__self__  # the instance
<__main__.MyClass object at 0x10f92b5f8>
>>> instance.get_class_attribute.__self__     # the class
<class '__main__.MyClass'>
Run Code Online (Sandbox Code Playgroud)