如何在子类中使用描述符的装饰器

kek*_*coh 5 python descriptor python-3.x python-descriptors

我想知道是否可以在子类中使用描述符的装饰器。

class Descriptor():
    def __get__(self, instance_obj, objtype):
        raise Exception('ouch.')
    def decorate(self, f):
        print('decorate', f)
        return f

class A():
    my_attr = Descriptor()

class B():
    @my_attr.decorate
    def foo(self):
        print('hey, whatsup?')

# --> NameError: name 'my_attr' is not defined
Run Code Online (Sandbox Code Playgroud)

这当然是行不通的,因为my_attr在 的类定义中未定义B

接下来我尝试了:

class B():
    @A.my_attr.decorate
    def foo(self):
        print('hey, whatsup?')

# --> Exception: ouch.
Run Code Online (Sandbox Code Playgroud)

但是,此方法调用描述符__get__方法(其中instance_obj参数为None),因此会触发测试异常。要访问装饰器,可以检查是否instance_obj返回None描述符本身:

def __get__(self, instance_obj, objtype):
    if instance_obj is None:
        return self
    raise Exception('avoid this')
# --> decorate <function B.foo at 0x1021dd7b8>
Run Code Online (Sandbox Code Playgroud)

有用!但这是否合理,或者是否有办法在 的类定义中使用装饰器B

Mar*_*ers 4

__dict__您可以通过从类的映射中检索原始对象来完全绕过描述符协议:

A.__dict__['my_attr'].decorate
Run Code Online (Sandbox Code Playgroud)

或更清洁,使用vars()

vars(A)['my_attr'].decorate
Run Code Online (Sandbox Code Playgroud)

但是,@装饰器语法不允许订阅(仅向您提供具有属性访问权限的更简单的表达式以及最后的单个调用),因此您必须首先提取字典:

_A_my_attr = vars(A)['my_attr']
@_A_my_attr.decorate
def foo(self):
    # ...
Run Code Online (Sandbox Code Playgroud)

但是,除非您必须捕获到类的绑定,否则最好保护第一个参数 being __get__None正如您发现的那样。这正是property对象或函数所做的事情。