在 Python 中,为什么 class 方法会覆盖实例方法?

In Python, why does a class method override an instance method?

查看下面的代码:

class MyClass:

    # instance method.
    def printline(self):
        print('This is an instance method!')

    @classmethod
    def printline(cls):
        print('This is a class method!')


# class MyClass ends.

obj = MyClass()
obj.printline()

输出:

This is a class method!

那么为什么 class 方法会覆盖实例方法呢?忽略我们可以简单地更改其中一个方法的名称,上面代码中如何访问实例方法?

函数的最新定义会屏蔽掉之前的定义。如果实例方法的定义类似于下面的第二个示例,您将调用它:

    In [1]: class MyClass:
       ...:
       ...:     # instance method.
       ...:     def printline(self):
       ...:         print('This is an instance method!')
       ...:
       ...:     @classmethod
       ...:     def printline(cls):
       ...:         print('This is a class method!')
       ...:

    In [2]: m =  MyClass()
    
    In [3]: m.printline()
    This is a class method!
    
    In [4]: class MyClass1:
       ...:
       ...:     @classmethod
       ...:     def printline(cls):
       ...:         print('This is a class method!')
       ...:
       ...:     # instance method.
       ...:     def printline(self):
       ...:         print('This is an instance method!')

    In [5]: m1 = MyClass1()
    
    In [6]: m1.printline()
    This is an instance method!

因为您定义了两次 printline,并且 稍后 定义获胜。 Class 方法和实例方法都只是 class 中的函数,它们没有单独的命名空间,因此在一个作用域中只能有一个具有给定名称的函数。

如果您查看MyClass的class字典,您可能会更清楚地了解:

>>> pp(MyClass.__dict__)
mappingproxy({'__dict__': <attribute '__dict__' of 'MyClass' objects>,
              '__doc__': None,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'MyClass' objects>,
              'printline': <classmethod object at 0x109d67dd0>})

在这里你会看到只有一个引用方法printline。那是因为你 over-rode 它 - 就像字典一样,MappingProxy 的键是唯一的。注意附加在它上面的 classmethod 值,如果你按照@hobbs 和@Arun 的建议去做,你会看到这个:

>>> MyClass.__dict__['printline']
<function MyClass.printline at 0x10979a710>

Class 函数定义总是放在 class 对象本身。装饰器实际上只是一种将具有命名描述符的对象重新分配给变量的快速方法。编写代码的较长方法是

class MyClass:

    # instance method.
    def printline(self):
        print('This is an instance method!')

    # make classmethod manually instead of using @classmethod
    def printline(cls):
        print('This is a class method!')
    printline = classmethod(printline)

obj = MyClass()
obj.printline()

第二个 printline 在成为函数的 classmethod 描述符后覆盖 class 对象上的第一个 printline