Точное объяснение от Армина Ронахера выше, расширяющее его ответы, чтобы новички, такие как я, хорошо его понимали:
Разница в методах, определенных в классе, будь то статический метод или метод экземпляра (есть еще один тип - метод класса - здесь не обсуждается, поэтому его можно пропустить), заключается в том, связаны ли они каким-либо образом с экземпляром класса или нет. Например, скажите, получает ли метод ссылку на экземпляр класса во время выполнения
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 в рамках моего ответа для краткости.
Если CPO является дескриптором, то интерпретатор python вызывает __get__()
метод CPO для доступа к значению, которое он содержит.
Чтобы определить, является ли CPO дескриптором, интерпретатор python проверяет, реализует ли он протокол дескриптора. Для реализации протокола дескриптора необходимо реализовать 3 метода
def __get__(self, instance, owner)
def __set__(self, instance, value)
def __delete__(self, instance)
например, для
>>> C.__dict__['foo'].__get__(c, C)
куда
self
- это CPO (это может быть экземпляр list, str, function и т. Д.) И предоставляется средой выполнения
instance
- это экземпляр класса, в котором определен этот CPO (объект 'c' выше), и он должен быть явно предоставлен нами.
owner
- это класс, в котором определен этот CPO (объект класса «C» выше), и он должен быть предоставлен нами. Однако это потому, что мы называем это CPO. когда мы вызываем его в экземпляре, нам не нужно предоставлять это, поскольку среда выполнения может предоставить экземпляр или его класс (полиморфизм)
value
- это предполагаемое значение CPO, которое должно быть предоставлено нами.
Не все 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 в c.foo(self)
необходим, потому что его сигнатура метода на самом деле это C.__dict__['foo'].__get__(c, C)
(как объяснено выше, C не требуется, так как он может быть обнаружен или преобразован). И это также то, почему вы получаете TypeError, если вы не передаете этот требуемый экземпляр аргумент.
Если вы заметили, что на метод по-прежнему ссылаются через объект класса C, а привязка к экземпляру класса достигается путем передачи контекста в форме объекта экземпляра в эту функцию.
Это довольно круто, поскольку, если вы выбрали отсутствие контекста или привязки к экземпляру, все, что нужно было, - это написать класс, который обернет дескриптор CPO и переопределит его __get__()
метод, чтобы он не требовал контекста. Этот новый класс мы называем декоратором и применяется с помощью ключевого слова @staticmethod
.
class C(object):
@staticmethod
def foo():
pass
Отсутствие контекста в новом обернутом CPO foo
не вызывает ошибки и может быть проверено следующим образом:
>>> C.__dict__['foo'].__get__(None, C)
<function foo at 0x17d0c30>
Вариант использования статического метода - это, скорее, пространство имен и ремонтопригодность кода (выведение его из класса и доступность во всем модуле и т. Д.).
Может быть, лучше писать статические методы, а не методы экземпляра, когда это возможно, если, конечно, вам не нужно контекстуализировать методы (например, переменные экземпляра доступа, переменные класса и т. Д.). Одна из причин - упростить сборку мусора, избегая нежелательных ссылок на объекты.
person
supi
schedule
18.09.2016
@classmethod
к определение. Первый параметр должен называтьсяcls
вместоself
, и он получит объект класса, а не экземпляр вашего класса:Test.method_three()
иa_test.method_three()
эквивалентны. - person Lutz Prechelt   schedule 04.08.2016self
? Есть ли у этого веский пример использования? - person alpha_989   schedule 11.01.2018