Почему в Python метод класса переопределяет метод экземпляра?

#python #overriding #class-method #instance-methods

#python #переопределение #class-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!
  

Итак, почему метод класса переопределяет метод экземпляра? Игнорируя тот факт, что мы можем просто изменить имя одного из методов, как получить доступ к методу экземпляра в приведенном выше коде?

Комментарии:

1. Потому что вы переопределяете его.

2. Как методы класса, так и методы экземпляра являются обычными атрибутами класса. У вас не может быть двух атрибутов с одинаковым именем.

Ответ №1:

Последнее определение функции будет маскировать предыдущее. Если бы метод экземпляра был определен как во 2-м примере ниже, вы бы вызывали его:

     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!

  

Ответ №2:

Потому что вы определили printline дважды, и выигрывает более позднее определение. Методы класса и методы экземпляра — это просто функции в классе, у них нет отдельных пространств имен, поэтому в области видимости может быть только одна функция с заданным именем.

Комментарии:

1. Итак, методы класса и методы экземпляра не имеют отдельных пространств имен, но переменные класса и переменные экземпляра имеют?

2. @ChinmayGhule Да. Все методы являются атрибутами класса; разница между методом экземпляра и методом класса заключается в том, что возвращает __get__ метод каждого из них. (Все методы реализованы через протокол дескриптора; см. docs.python.org/3/howto/descriptor.html для получения дополнительной информации.)

Ответ №3:

Вы могли бы получить более четкое представление, если бы заглянули в словарь классов MyClass :

 >>> 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 . Это потому, что вы переборщили с ним — как и со словарем, ключи a MappingProxy уникальны. Обратите внимание на classmethod значение, придаваемое этому, если бы вы сделали то, что предложили @hobbs и @Arun, вы бы увидели это:

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

Ответ №4:

Определения функций класса всегда размещаются в самом объекте класса. Декораторы — это на самом деле просто быстрый способ переназначить объект с именованным дескриптором переменной. Более длинный способ написания вашего кода — это

 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 перезаписал первый printline в объекте класса после того, как стал classmethod дескриптором функции.