Точне пояснення від Арміна Ронахера вище, розширюючи його відповіді, щоб новачки, як я, добре розуміли це:
Різниця в методах, визначених у класі, будь то статичний або екземплярний метод (є ще один метод класу типу - тут не обговорюється, тому пропускаючи його), полягає в тому, чи вони якимось чином пов'язані з екземпляром класу чи ні. Наприклад, скажіть, чи отримує метод посилання на екземпляр класу під час виконання
class C:
a = []
def foo(self):
pass
C # this is the class object
C.a # is a list object (class property object)
C.foo # is a function object (class property object)
c = C()
c # this is the class instance
__dict__Словник властивість об'єкта класу містить посилання на всі властивості і методи об'єкта класу і , таким чином
>>> C.__dict__['foo']
<function foo at 0x17d05b0>
метод foo доступний, як зазначено вище. Тут важливо зазначити, що все в python є об'єктом, і тому посилання в словнику вище самі вказують на інші об'єкти. Дозвольте мені назвати їх об'єкти властивості класу - або як СРО в межах моєї відповіді на стислість.
Якщо CPO є дескриптором, то інтерпретатор python викликає __get__()метод CPO для доступу до значення, яке він містить.
Щоб визначити, чи є ЦРО дескриптором, інтерпретатор python перевіряє, чи реалізує протокол дескриптора. Для реалізації дескрипторного протоколу є реалізація 3 методів
def __get__(self, instance, owner)
def __set__(self, instance, value)
def __delete__(self, instance)
напр
>>> C.__dict__['foo'].__get__(c, C)
де
self є СРО (це може бути екземпляр списку, str, функції тощо) і постачається під час виконання
instance - це екземпляр класу, де цей CPO визначений (об'єкт 'c' вище) і повинен бути наданий нам explicity
owner- це клас, де цей CPO визначений (об'єкт класу 'C' вище) і його потрібно поставити нами. Однак це тому, що ми називаємо це на СРО. коли ми називаємо це екземпляром, нам не потрібно подавати це, оскільки час виконання може постачати екземпляр або його клас (поліморфізм)
value - це цільове значення для СРО і має бути надане нами
Не всі CPO є дескрипторами. Наприклад
>>> C.__dict__['foo'].__get__(None, C)
<function C.foo at 0x10a72f510>
>>> C.__dict__['a'].__get__(None, C)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__get__'
Це тому, що клас списку не реалізує протокол дескриптора.
Таким чином, аргумент self in c.foo(self)потрібен, оскільки його підпис методу насправді такийC.__dict__['foo'].__get__(c, C) (як пояснено вище, C не потрібен, оскільки його можна виявити або поліморфно). І саме тому ви отримуєте TypeError, якщо не передаєте потрібний аргумент екземпляра.
Якщо ви помітили, що метод все ще посилається через клас Object C, а зв'язування з екземпляром класу досягається шляхом передачі контексту у формі об'єкта екземпляра в цю функцію.
Це досить приголомшливо, оскільки якщо ви вирішили не зберігати жодний контекст або не прив'язувати його до екземпляра, все, що потрібно, було написати клас, щоб обернути дескрипторну СРО та змінити її __get__()метод, щоб не вимагати контексту. Цей новий клас - це те, що ми називаємо декоратором, і застосовується за допомогою ключового слова@staticmethod
class C(object):
@staticmethod
def foo():
pass
Відсутність контексту в новому завершеному CPO fooне призводить до помилки, і його можна перевірити наступним чином:
>>> C.__dict__['foo'].__get__(None, C)
<function foo at 0x17d0c30>
Випадок використання статичного методу є більшою частиною простору імен та ремонту коду (виведення його з класу та надання доступу до модуля тощо).
Можливо, краще писати статичні методи, а не методи екземплярів, коли це можливо, якщо, звичайно, вам не потрібно контекстуалізувати методи (наприклад, змінні екземпляри доступу, змінні класу тощо). Одна з причин - полегшити збирання сміття, не зберігаючи небажані посилання на предмети.