С точки зрения «пользователя», метод класса в Python - это метод, который получает свой класс в качестве своего первого параметра - в отличие от «обычных» методов, которые получают экземпляр класса в качестве своего первого параметра - который по соглашениюnamed 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>
Если вы хотите получить объект функции, без преобразования в объект метода, вы можете сделать это через класс *Атрибут 1031 *, который не вызывает дескриптор:
>>> 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
в качестве первого параметра при вызове.