Различия методов классов в Python: связанные, несвязанные и статические
в чем разница между следующими методами класса?
это то, что один статичен, а другой нет?
class Test(object):
def method_one(self):
print "Called method_one"
def method_two():
print "Called method_two"
a_test = Test()
a_test.method_one()
a_test.method_two()
10 ответов:
в Python, существует различие между привязан и unbound методы.
в основном, вызов функции-члена (например,
method_one), связанного функцияa_test.method_one()переведен на
Test.method_one(a_test)т. е. вызов несвязанного метода. Из-за этого, вызов вашей версии
method_twoне будетTypeError>>> a_test = Test() >>> a_test.method_two() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: method_two() takes no arguments (1 given)вы можете изменить поведение метода, используя декоратор
class Test(object): def method_one(self): print "Called method_one" @staticmethod def method_two(): print "Called method two"декоратор сообщает встроенный метакласс по умолчанию
type(класс класса, ср. этот вопрос) чтобы не создавать связанные методы дляmethod_two.теперь, вы можете вызвать статический метод на экземпляр класса напрямую:
>>> a_test = Test() >>> a_test.method_one() Called method_one >>> a_test.method_two() Called method_two >>> Test.method_two() Called method_two
методы в Python-это очень, очень простая вещь, как только вы поняли основы системы дескрипторов. Представьте себе следующий класс:
class C(object): def foo(self): passтеперь давайте посмотрим на этот класс в оболочке:
>>> C.foo <unbound method C.foo> >>> C.__dict__['foo'] <function foo at 0x17d05b0>как вы можете увидеть, если вы получаете доступ к
fooатрибут на класс вы получаете обратно несвязанный метод, однако внутри класса хранения (dict) есть функция. Почему это? Причина этого заключается в том, что класс ваш класс реализует__getattribute__это разрешает дескрипторы. Звучит сложно, но это не.C.fooпримерно эквивалентно этому коду в этом частном случае:>>> C.__dict__['foo'].__get__(None, C) <unbound method C.foo>это потому, что функции у
__get__метод, который делает их дескрипторы. Если у вас есть экземпляр класса, это почти то же самое, что простоNone- это экземпляр класса:>>> c = C() >>> C.__dict__['foo'].__get__(c, C) <bound method C.foo of <__main__.C object at 0x17bd4d0>>теперь почему питон делает это? Поскольку объект метода связывает первый параметр функции с экземпляром класса. Вот откуда приходит самость. Теперь иногда вы не хотите, чтобы ваш класс сделал функцию методом, вот где
staticmethodвступает в игру:class C(object): @staticmethod def foo(): passThe
staticmethodдекоратор обертывает ваш класс и реализует манекен__get__это возвращает обернутую функцию как функцию, а не как метод:>>> C.__dict__['foo'].__get__(None, C) <function foo at 0x17d0c30>надеюсь, что это объясняет.
>>> class Class(object): ... def __init__(self): ... self.i = 0 ... def instance_method(self): ... self.i += 1 ... print self.i ... c = 0 ... @classmethod ... def class_method(cls): ... cls.c += 1 ... print cls.c ... @staticmethod ... def static_method(s): ... s += 1 ... print s ... >>> a = Class() >>> a.class_method() 1 >>> Class.class_method() # The class shares this value across instances 2 >>> a.instance_method() 1 >>> Class.instance_method() # The class cannot use an instance method Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unbound method instance_method() must be called with Class instance as first argument (got nothing instead) >>> Class.instance_method(a) 2 >>> b = 0 >>> a.static_method(b) 1 >>> a.static_method(a.c) # Static method does not have direct access to >>> # class or instance properties. 3 >>> Class.c # a.c above was passed by value and not by reference. 2 >>> a.c 2 >>> a.c = 5 # The connection between the instance >>> Class.c # and its class is weak as seen here. 2 >>> Class.class_method() 3 >>> a.c 5
при вызове члена класса Python автоматически использует ссылку на объект в качестве первого параметра. Переменная
selfна самом деле ничего не значит, это просто конвенции кодирования. Вы могли бы назвать этоgargalooЕсли вы хотели. Тем не менее, призыв кmethod_twoсоздатьTypeError, потому что Python автоматически пытается передать параметр (ссылку на его родительский объект) методу, который был определен как не имеющий параметров.на самом деле заставить его работать, вы могли бы добавьте это к определению класса:
method_two = staticmethod(method_two)или вы могли бы использовать
@staticmethodфункции оформителя.
method_two не будет работать, потому что вы определяете функцию-член, но не говорите ей, что функция является членом. Если вы выполните последнюю строку, вы получите:
>>> a_test.method_two() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: method_two() takes no arguments (1 given)Если вы определяете функции-члены для класса, первый аргумент всегда должен быть "self".
точное объяснение от Армина Ронахера выше, расширяя его ответы, чтобы новички, такие как я, хорошо это понимали:
разница в методах, определенных в классе, будь то статический метод или метод экземпляра (есть еще один метод типа - класса - не обсуждается здесь, поэтому пропуская его), заключается в том, связаны ли они каким-либо образом с экземпляром класса или нет. Например, скажите, получает ли метод ссылку на экземпляр класса во время время работы
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 instanceThe
__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 (это может быть экземпляр список, str, функция и т. д.) и поставляется средой выполнения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__'это связано с тем, что класс list не реализует протокол дескриптора.
таким образом, аргумент self in
c.foo(self)не требуется, поскольку его сигнатура метода на самом деле этоC.__dict__['foo'].__get__(c, C)(как указано выше, не требуется, как это можно узнать или свиток) А это уже также почему вы получаете TypeError, если вы не передаете этот обязательный аргумент экземпляра.если вы заметили, что на метод все еще ссылаются через объект класса C и привязка с экземпляром класса достигается путем передачи контекста в виде объекта экземпляра в эту функцию.
это довольно удивительно, так как если вы решили не сохранять контекст или привязку к экземпляру, все, что нужно было написать класс, чтобы обернуть дескриптор CPO и переопределить его
__get__()метод не требует контекста. Этот новый класс является то, что мы называем декоратор и применяется через ключевое слово@staticmethodclass C(object): @staticmethod def foo(): passотсутствие контекста в новом обернутом CPO
fooне выдавать ошибку и может быть проверена следующим образом:>>> C.__dict__['foo'].__get__(None, C) <function foo at 0x17d0c30>случай использования статического метода-это скорее пространство имен и поддержка кода(вынимание его из класса и предоставление его в модуле и т. д.).
может быть, лучше написать статические методы, а не методы экземпляра, когда это возможно, если, конечно, вам не нужно контекстуализировать методы(например, переменные экземпляра access, переменные класса и т. д.). Одна из причин заключается в том, чтобы облегчить сбор мусора, не сохраняя нежелательные ссылки на объекты.
это ошибка.
во-первых, первая строка должна быть такой (будьте внимательны из столиц)
class Test(object):всякий раз, когда вы вызываете метод класса, он получает себя в качестве первого аргумента (отсюда имя self) и method_two дает эту ошибку
>>> a.method_two() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: method_two() takes no arguments (1 given)
второй не будет работать, потому что когда вы вызываете его так, как python внутренне пытается вызвать его с экземпляром a_test в качестве первого аргумента, но ваш method_two не принимает никаких аргументов, поэтому он не будет работать, вы получите ошибку времени выполнения. Если вам нужен эквивалент статического метода, вы можете использовать метод класса. Существует гораздо меньше необходимости в методах класса в Python, чем статические методы в таких языках, как Java или C#. Чаще всего лучшим решением является использование метода в модуле, вне a определение класса, те работают более эффективно, чем методы класса.
вызов method_two вызовет исключение для непринятия параметра self среда выполнения Python автоматически передаст его.
Если вы хотите создать статический метод в классе Python, украсьте его с помощью
staticmethod decorator.Class Test(Object): @staticmethod def method_two(): print "Called method_two" Test.method_two()
пожалуйста, прочитайте эту документацию из Guido первый класс все ясно объяснил, как рождаются несвязанные, связанные методы.
Comments