Python中的装饰器@staticmethod是如何去掉实例对象的?

How does the decorator @staticmethod in Python get rid of the instance object?

据我了解,装饰器是一个函数,它将另一个函数作为参数,对其执行一些操作,然后 returns 它。

说到装饰器@staticmethod,装饰器到底做了什么来消除默认传递的实例对象?

staticmethod装饰器returns一个staticmethod对象。此对象实现 descriptor protocol,与函数相同。

这样做并不是要摆脱实例,而是 staticmethod.__get__ 完全忽略 绑定,而只是 returns 未更改的函数对象。对于常规函数,function.__get__ 将改为通过返回方法对象进行绑定(然后跟踪实例和函数以在调用时将它们组合起来)。

您可以通过手动调用描述符协议来重现此内容:

>>> class Demo:
...     def regular(self):
...         pass
...     @staticmethod
...     def static():
...         pass
...
>>> Demo.__dict__['regular']  # bypass __getattribute__
<function Demo.regular at 0x108515268>
>>> Demo.__dict__['static']   # bypass __getattribute__
<staticmethod object at 0x1084d4f60>
>>> Demo.__dict__['regular'].__get__(Demo())  # descriptor protocol, pass in an instance
<bound method Demo.regular of <__main__.Demo object at 0x1084e2668>>
>>> Demo.__dict__['static'].__get__(Demo())  # descriptor protocol, pass in an instance
<function Demo.static at 0x1085152f0>

通过Demo.__dict__访问Democlass的属性,我们绕过了通常由__getattribute__方法强制执行的描述符协议。如您所见,对于常规方法,返回的是 function 对象,但对于 static,则找到了 staticmethod 对象。

调用 .__get__(Demo()) 来调用描述符协议,然后分别生成一个方法对象和未更改的函数对象。这正是直接访问实例上的相同名称所产生的结果:

>>> Demo().regular
<bound method Demo.regular of <__main__.Demo object at 0x1084dde10>>
>>> Demo().static
<function Demo.static at 0x1085152f0>

请注意,相同的协议也是 classmethod 个对象被传递 type(instance) 而不是实例作为第一个参数的原因,也是 property 个对象调用底层函数的原因正在访问。