我有这段代码:
class ABC:
def test():
print('Hi there')
ABC.test()
哪个输出:
Hi there
虽然失败了:
ABC().test()
TypeError: test() takes 0 positional arguments but 1 was given
# Makes sense
我们知道当我们调用像classmethod
这样的<classname>.<methodname>()
时,该类被隐式地作为参数传递给它,但test
这里没有参数。
为什么我们不会因为意外的争论而看到TypeError
?这个类不是作为test
的参数传递的吗?
没有任何内容传递给test()
,因为函数在访问类时不会绑定任何东西。它仍然是未绑定的,您将获得原始功能:
>>> class ABC:
... def test():
... print('Hi there')
...
>>> ABC.test
<function ABC.test at 0x1082706c0>
只要传入正确数量的参数,就可以直接调用函数。这是0,所以ABC.test()
成功了。
它不是classmethod
,需要用@classmethod
进行装饰(或以其他方式生成classmethod
对象并将其存储为类的属性),此时访问属性将函数绑定到类对象,生成绑定的method
:
>>> class ABC:
... @classmethod
... def test():
... print('Hi there')
...
>>> ABC.test
<bound method ABC.test of <class '__main__.ABC'>>
调用此结果会导致异常:
>>> ABC.test()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: test() takes 0 positional arguments but 1 was given
函数也可以绑定,但仅限于在类的实例上访问时。 function
和classmethod
对象都实现了descriptor protocol,访问类和实例上的属性将触发描述符上的__get__
方法。但是在这种情况下只有classmethod
对象返回绑定方法,函数只返回自己。
具体来说,descriptor __get__
method通过None
作为第一个参数,绑定的对象,当属性访问在类上时,以及实例在实例上访问时。 classmethod().__get__()
忽略实例参数并生成一个绑定到第二个参数(owner
)的方法对象。当第一个参数是self
时,函数返回None
,否则返回绑定到实例的方法对象:
>>> def foo(boundto='nothing'):
... print(f'Bound to {boundto}')
...
>>> foo.__get__('instance', 'owner')()
Bound to instance
>>> foo.__get__(None, 'owner')()
Bound to nothing
>>> classmethod(foo).__get__('instance', 'owner')()
Bound to owner
>>> classmethod(foo).__get__(None, 'owner')()
Bound to owner