gpt4 book ai didi

python - @classmethod 没有调用我的自定义描述符的 __get__

转载 作者:太空宇宙 更新时间:2023-11-03 13:08:20 25 4
gpt4 key购买 nike

我有一个装饰器叫做 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__ , 它绑定(bind)底层函数并将绑定(bind)方法传递给 Special 的新实例- 这就是为什么 Foo().bar()Foo().bar.special()工作。

  • Foo.baz只是返回原来的 Special实例 - 常规调用和特殊调用都很简单。

  • Foo.qux无需调用我的 __get__ 即可绑定(bind).

    • 新的绑定(bind)对象知道在被直接调用时将类作为第一个参数传递 - 所以 Foo.qux()有效。
    • Foo.qux.special只是调用 .special底层函数(classmethod 不知道如何绑定(bind)它)- 所以 Foo.qux.special()正在调用未绑定(bind)函数,因此 TypeError .

Foo.qux.special有什么办法吗?知道它是从 classmethod 调用的?或者解决这个问题的其他方法?

最佳答案

classmethod 是一个返回绑定(bind)方法的描述符。它不会在此过程中调用您的 __get__ 方法,因为它不能在不破坏描述符协议(protocol)的某些约定的情况下这样做。 (也就是说,instance 应该是一个实例,而不是一个类。)所以您的 __get__ 方法没有被调用是完全可以预料的。

那么如何让它发挥作用呢?好吧,想一想:您希望 some_instance.barSomeClass.bar 都返回一个 Special 实例。为了实现这一点,您只需应用 @Special 装饰器 last:

class Foo:
@Special
@staticmethod
def baz():
return 'baz'

@Special
@classmethod
def qux(cls):
return 'qux'

这使您可以完全控制是否/何时/如何调用装饰函数的描述符协议(protocol)。现在您只需要删除 if instance is None: 方法中的 __get__ 特例,因为它会阻止类方法正常工作。 (原因是 classmethod 对象是不可调用的;你必须调用描述符协议(protocol)将 classmethod 对象变成一个可以调用的函数。)换句话说,Special.__get__ 方法必须无条件调用修饰函数的 __get__ 方法,如下所示:

def __get__(self, instance=None, owner=None):
return Special(self.func.__get__(instance, owner))

现在您所有的断言都将通过。

关于python - @classmethod 没有调用我的自定义描述符的 __get__,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50847358/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com