从“用户”的角度来看,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
作为调用的第一个参数填充。