python - @classmethod 没有调用我的自定义描述符的 __get__
问题描述
我有一个名为的装饰器Special
,它将一个函数转换为它自己的两个版本:一个可以直接调用并在结果前面加上前缀'regular '
,另一个可以.special
在结果前面加上前缀'special '
:
class Special:
def __init__(self, func):
self.func = func
def __get__(self, instance, owner=None):
if instance is None:
return self
return Special(self.func.__get__(instance, owner))
def special(self, *args, **kwargs):
return 'special ' + self.func(*args, **kwargs)
def __call__(self, *args, **kwargs):
return 'regular ' + self.func(*args, **kwargs)
它适用于常规方法和静态方法 - 但.special
不适用于类方法:
class Foo:
@Special
def bar(self):
return 'bar'
@staticmethod
@Special
def baz():
return 'baz'
@classmethod
@Special
def qux(cls):
return 'qux'
assert Foo().bar() == 'regular bar'
assert Foo().bar.special() == 'special bar'
assert Foo.baz() == 'regular baz'
assert Foo.baz.special() == 'special baz'
assert Foo.qux() == 'regular qux'
assert Foo.qux.special() == 'special qux' # TypeError: qux() missing 1 required positional argument: 'cls'
Foo().bar
正在调用__get__
,它绑定底层函数并将绑定的方法传递给一个新的实例Special
- 这就是Foo().bar()
和Foo().bar.special()
工作的原因。Foo.baz
只是返回原始Special
实例 - 常规和特殊调用都很简单。Foo.qux
在不调用 my 的情况下具有约束力__get__
。- 新的绑定对象知道在直接调用时将类作为第一个参数传递 - 所以
Foo.qux()
有效。 Foo.qux.special
只是调用.special
底层函数的 (classmethod
不知道如何绑定它)-Foo.qux.special()
调用未绑定函数也是如此,因此TypeError
.
- 新的绑定对象知道在直接调用时将类作为第一个参数传递 - 所以
有什么方法Foo.qux.special
可以知道它是从 a 调用的classmethod
吗?或者其他解决这个问题的方法?
解决方案
classmethod
是返回绑定方法的描述符。它不会__get__
在此过程中调用您的方法,因为它不能在不破坏描述符协议的某些合同的情况下这样做。(即,instance
应该是一个实例,而不是一个类。)所以你的__get__
方法没有被调用是完全可以预料的。
那么如何让它发挥作用呢?好吧,想一想:你想要两者some_instance.bar
并SomeClass.bar
返回一个Special
实例。为了实现这一点,您只需最后@Special
应用装饰器:
class Foo:
@Special
@staticmethod
def baz():
return 'baz'
@Special
@classmethod
def qux(cls):
return 'qux'
这使您可以完全控制是否/何时/如何调用装饰函数的描述符协议。现在您只需要删除方法中的if instance is None:
特殊情况__get__
,因为它会阻止类方法正常工作。(原因是 classmethod 对象不可调用;您必须调用描述符协议才能将 classmethod 对象转换为可以调用的函数。)换句话说,该Special.__get__
方法必须无条件地调用修饰函数的__get__
方法,如下所示:
def __get__(self, instance=None, owner=None):
return Special(self.func.__get__(instance, owner))
现在你所有的断言都会通过。
推荐阅读
- c# - 有没有办法根据 MS SQL 服务器数据库记录的更改自动更新弹性搜索索引?
- mongodb - 查询mongo检测时间序列的值变化
- ipython - 如何在 jupyter 笔记本中插入制表符而不是自动完成?
- rust - 如何与超请求处理程序共享不可变的配置数据?
- r - 为什么绘图窗口的大小在 R 中保持不变?
- docker - 无法在 Websphere docker 中连接数据源:java.lang.ClassNotFoundException:DSRA8000E
- mocha.js - 如何使用 wdio mocha 在@wdio/allure-reporter 中添加步骤
- jquery - 日期选择器 - 更改格式
- javascript - 如何修复 Fabric Js 和 RemoveColor 问题?
- dynamics-crm - 是否可以在 Dynamics crm 中为 powermap 插件提供更多或/和自定义引脚?