实例方法的Python装饰器可以访问该类吗?

2024-04-23 19:18:29 发布

您现在位置:Python中文网/ 问答频道 /正文

嗨,我有一些大致如下的东西。基本上,我需要从decorator访问实例方法的类,decorator用于实例方法的定义中。

def decorator(view):
    # do something that requires view's class
    print view.im_class
    return view

class ModelA(object):
    @decorator
    def a_method(self):
        # do some stuff
        pass

代码如下:

AttributeError: 'function' object has no attribute 'im_class'

我发现了类似的问题/答案-Python decorator makes function forget that it belongs to a classGet class in Python decorator-但是这些问题/答案依赖于一个解决方案,该解决方案通过获取第一个参数在运行时获取实例。在我的例子中,我将基于从类中收集到的信息来调用该方法,因此我不能等待调用的到来。


Tags: 实例方法答案viewthat定义objectdef
3条回答

如果您使用的是Python2.6或更高版本,则可以使用类装饰器,可能类似于这样(警告:未测试的代码)。

def class_decorator(cls):
   for name, method in cls.__dict__.iteritems():
        if hasattr(method, "use_class"):
            # do something with the method and class
            print name, cls
   return cls

def method_decorator(view):
    # mark the method as something that requires view's class
    view.use_class = True
    return view

@class_decorator
class ModelA(object):
    @method_decorator
    def a_method(self):
        # do some stuff
        pass

方法decorator通过添加一个“use_class”属性将该方法标记为感兴趣的方法-函数和方法也是对象,因此可以将其他元数据附加到它们。

创建类之后,类装饰器将遍历所有方法,并对已标记的方法执行所需的任何操作。

如果您希望所有的方法都受到影响,那么您可以省略方法decorator,而只使用类decorator。

正如其他人指出的,在调用decorator时还没有创建类。但是,可以用decorator参数注释函数对象,然后在元类的__new__方法中重新装饰函数。您需要直接访问函数的__dict__属性,至少对我来说,func.foo = 1导致了AttributeError。

由于Python3.6,您可以使用^{}以非常简单的方式完成这一任务。文档声明__set_name__“在创建所属类所有者时调用”。 下面是一个例子:

class class_decorator:
    def __init__(self, fn):
        self.fn = fn

    def __set_name__(self, owner, name):
        # do something with owner, i.e.
        print(f"decorating {self.fn} and using {owner}")
        self.fn.class_name = owner.__name__

        # then replace ourself with the original method
        setattr(owner, name, self.fn)

注意,它在类创建时被调用:

>>> class A:
...     @class_decorator
...     def hello(self, x=42):
...         return x
...
decorating <function A.hello at 0x7f9bedf66bf8> and using <class '__main__.A'>
>>> A.hello
<function __main__.A.hello(self, x=42)>
>>> A.hello.class_name
'A'
>>> a = A()
>>> a.hello()
42

如果您想进一步了解类是如何创建的,特别是当调用__set_name__时,可以参考documentation on "Creating the class object"

相关问题 更多 >