繁体   English   中英

在描述符中,__call__是否可以访问使用装饰器的类?

[英]In a descriptor, does __call__ have access to the class where the decorator is being used?

考虑以下代码:

class MyCustomDescriptor:
    def __init__(self,foo):
        self._foo = foo

    def __call__(self,decorated_method):
        # Here's my question...  Is there any way to get a reference to the
        # type (ClassA or ClassB) here?
        return self

    def __get__(self,instance,type):
        # Clearly at this point I can get the type of the class.
        # But it's too late, I would have liked
        # to get it back in __call__.
        return 10

class ClassA:
    @MyCustomDescriptor(foo=1)
    def some_value(self): pass

class ClassB:
    @MyCustomDescriptor(foo=1)
    def some_value(self): pass

我想获得对该类的引用的原因是,我想使用修饰的函数/方法向该类添加一些静态数据。 我意识到这有点不典型,但对于我正在做的事情,这将有所帮助。

解答-无法完成。 基于以下响应之一,我从调用内检查了堆栈,并能够获取使用描述符的完全合格的类(在我的示例中为ClassA或ClassB)。 但是您不能将其转换为类型/类,因为类型/类仍在解析中(或python中正确的术语)。 换句话说,python遇到了ClassA并开始对其进行解析。 解析它时,它会遇到描述符并调用init并在描述符上调用。 ClassA仍未完成解析。 因此,无论您可以从调用中获取完全限定的模块/类名称,都不能将其转换为类型。

在应用装饰器的时候, some_value只是一个函数,而不是一个方法。 因此,不,该函数无法知道它与特定类相关联。

两种选择是:

  • 将类名传递给MyCustomDescriptor (以及foo ),或者
  • 使用类装饰器创建描述符some_value

类装饰器可能看起来像这样:

def register(method_name,foo):
    def class_decorator(cls):
        method=getattr(cls,method_name)
        class MyCustomDescriptor(object):
            def __get__(self,instance,type):
                result=method(instance)
                return '{c}: {r}'.format(c=cls.__name__,r=result)
        setattr(cls,method_name,MyCustomDescriptor())
        return cls
    return class_decorator

@register('some_value',foo=1)
class ClassA:
    def some_value(self):
        return 10

例如,跑步

a=ClassA()
print(a.some_value)

产量

ClassA: 10

好吧...我可以想到一种方法,但是它相当于我喜欢的“ Python voodoo”,这意味着它正在访问Python的功能,这些功能不应在常规编程中使用。 因此,在执行此操作之前请仔细考虑 它也依赖于实现,因此如果您希望代码可移植到其他Python解释器(CPython除外),请不要依赖于此。 话虽如此:

调用描述符的__call__方法时,可以使用inspect.stack()访问解释器堆栈。 返回列表中的第二个堆栈帧表示从中__call__的上下文。 该堆栈框架中包含的部分信息是上下文名称,该名称通常是一个函数名称,但是在这种情况下__call__不是从函数内部调用的,而是从类内部调用的,因此上下文名称将是班级名称。

import inspect

class MyCustomDescriptor:
    def __call__(self,decorated_method):
        self.caller_name = inspect.stack()[1][3]
        return self
    ...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM