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

Posted

技术标签:

【中文标题】Python 中的装饰器@staticmethod 是如何摆脱实例对象的?【英文标题】:How does the decorator @staticmethod in Python get rid of the instance object? 【发布时间】:2017-11-01 06:56:06 【问题描述】:

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

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

【问题讨论】:

查看底部某处的 Python 实现:docs.python.org/3/howto/… 【参考方案1】:

staticmethod 装饰器返回一个staticmethod 对象。这个对象实现了descriptor protocol,和函数一样。

它所做的不是摆脱实例,而是完全绑定staticmethod.__get__ ignores 并返回未更改的函数对象。对于常规函数,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__ 访问Demo 类的属性,我们绕过了通常由__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 对象在访问时调用底层函数的原因。

【讨论】:

以上是关于Python 中的装饰器@staticmethod 是如何摆脱实例对象的?的主要内容,如果未能解决你的问题,请参考以下文章

python之内置装饰器(property/staticmethod/classmethod)

面向对象之classmethod和staticmethod(python内置装饰器)

知识点 - python 装饰器@staticmethod和@classmethod区别和使用

python三个自带装饰器的功能与使用(@property@staticmethod@classmethod)

python 装饰器语法糖(@classmethod @staticmethod @property @name.)原理剖析和运用场景

python9-类的装饰器(property, classmethod, staticmethod)