Документ модели данных Python: несвязанный пользовательский объект метода и объект метода класса

В справочной модели данных автор приложил немало усилий, объясняя, как создаются и управляются пользовательские методы:(см. http://docs.python.org/reference/datamodel.html и roll вниз)

Пользовательские объекты методов могут быть созданы при получении атрибута класса (возможно, через экземпляр этого класса), если этот атрибут является объектом пользовательской функции, несвязанным объектом пользовательского метода или методом класса объект. Когда атрибут является определяемым пользователем объектом метода, новый объект метода создается только в том случае, если класс, из которого он извлекается, совпадает или является производным классом класса, сохраненного в исходном объекте метода; в противном случае исходный объект метода используется как есть.

Так в чем же разница между несвязанным пользовательским объектом метода и объектом метода класса?

1 ответ

Решение

С точки зрения "пользователя", метод класса в Python - это метод, который получает свой класс в качестве первого параметра - в отличие от "обычных" методов, которые получают экземпляр класса в качестве первого параметра - который по соглашению называется self,

Если вы извлекаете "обычный" метод из класса, а не из набора этого класса, вы получаете "несвязанный метод" - то есть объект, который является оберткой для функции, но который автоматически не добавляет ни сам класс и ни один экземпляр в качестве первого параметра при его вызове. Поэтому, если вы хотите вызвать "несвязанный метод", вы должны вручную передать экземпляр его класса в качестве первого параметра.

Если вы вручную вызываете метод класса, с другой стороны, класс заполняется как первый параметр для вас:

>>> class A(object):
...   def b(self):
...      pass
...   @classmethod
...   def c(cls):
...      pass
... 
>>> A.b
<unbound method A.b>
>>> A.c
<bound method type.c of <class '__main__.A'>>
>>> A.c()
>>> A.b()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method b() must be called with A instance as first argument (got nothing instead)
>>> 

Под капотом все более или менее похоже на "новые классы стиля":

Когда кто-то определяет тело класса, методы - это просто обычные функции - когда тело класса закончено, Python вызывает метакласс класса (который обычно является встроенным type) - и передайте ему в качестве параметров имя, базовые классы и словарь тела класса. Этот вызов дает класс, который в Python является объектом, который является классом, поскольку все является объектом.

Теперь у Python есть несколько изящных способов настройки доступа к атрибутам - так называемые "дескрипторы". Дескриптор - это любой объект, который определяет метод с именем __get__ (или же __set__ или же __del__ но нас это не волнует) Когда кто-то обращается к атрибуту класса или объекта в Python, возвращается объект, на который ссылается этот атрибут, за исключением случаев, когда это атрибут класса, а объект является дескриптором. В этом случае вместо того, чтобы возвращать сам объект, Python вызывает __get__ метод для этого объекта, и вместо этого возвращает его результаты. Например, property встроенный просто класс, который реализует как __set__, __get__ а также __del__ по мере необходимости.

Теперь, что происходит, когда атрибут извлекается, это то, что любая функция (или метод класса или несвязанный метод, как утверждает модель данных) в его теле, имеет __get__ метод, который делает его дескриптором. По сути, дескриптор, который при каждом доступе к атрибуту извлекает объект, названный как функция, как она определена в теле функции, создает новый объект вокруг этой функции - объект, который при вызове будет автоматически заполнять первый параметр - сказать, method,

Напр.:

>>> class B(object):
...    def c(self):
...      pass
...    print c
... 
<function c at 0x1927398>
>>> print B.c
<unbound method B.c>
>>> b = B()
>>> b.c
<bound method B.c of <__main__.B object at 0x1930a10>

Если вы хотите получить объект функции без преобразования в объект метода, вы можете сделать это через класс __dict__ атрибут, который не вызывает дескриптор:

>>> B.__dict__["c"]
<function c at 0x1927398>
>>> B.__dict__["c"].__get__
<method-wrapper '__get__' of function object at 0x1927398>
>>> B.__dict__["c"].__get__(b, B)
<bound method B.c of <__main__.B object at 0x1930a10>>
>>> B.__dict__["c"].__get__(None, B)
<unbound method B.c>

Что касается "методов класса", то это просто разные типы объектов, которые явно украшены встроенными classmethod - Объект, который он возвращает, когда его __get__ называется обертка вокруг оригинальной функции, которая будет заполнять cls в качестве первого параметра по вызову.

Другие вопросы по тегам