Python装饰器是否通过实例属性进行参数化?

时间:2015-05-04 18:31:59

标签: python decorator

我正在尝试为类方法(my_decorator)定义一个python装饰器(f),如下面的简化场景所示。 my_decoratorparam进行参数化,这取决于类属性(在本例中为level)。

class my_decorator:
    def __init__(self, param):
        self.param = param

    def __call__(self, f):
        def f_decorated(instance, c):
            print("decorated with param = %d" % self.param)
            return f(c)

        return f_decorated


class A:
    def __init__(self, level):
        self.level = level

    @my_decorator(param=self.level)   # Here is the problematic line!
    def f(x):
        return x

if __name__ == "__main__":
    a = A(level=2)
    a.f(1)   # name "self" is not defined 

上面的代码不起作用,我得到一个“自我”没有定义错误。所以我的问题是,有没有办法实现上下文参数化装饰器的目标?

BTW,用例是:我试图实现持久的memoization技术(描述于 memoize to disk - python - persistent memoization) 缓存持续存在的文件取决于类A,特别是“级别”。例如,我想保留文件cache_%d.txt % self.level

3 个答案:

答案 0 :(得分:1)

陈,

装饰器在编译时或导入期间执行,因为在导入期间执行了类主体。因此,如果您在不创建该类实例的情况下执行您的代码段,也会抛出错误。

除了参数self.level在装饰器内部以外,对我来说没有多大意义,因为它是一个实例变量,所以你可以直接在函数f(x)中使用:

以下是一些更多细节:

Python decorator function called at compile time

答案 1 :(得分:1)

正如错误所述,self此时并不存在。这应该是清楚的:self仅作为方法的参数存在,并且您当时甚至不在方法中。装饰器与所有类级代码一样,在定义时进行评估。

我不完全确定您要实现的目标,但您可以使用字符串以及getattr

class my_decorator:
    def __init__(self, param_name):
        self.param_name = param_name

    def __call__(self, f):
        def f_decorated(instance, c):
            param = getattr(instance, self.param_name)
            print("decorated with param = %d" % param)
            return f(c)

...

class A:
    def __init__(self, level):
        self.level = level

    @my_decorator(param_name='level')
    def f(x):
        return x

答案 2 :(得分:1)

self是一个变量。它只在方法内部定义,装饰器在外面。如果您需要装饰器内对象的属性,则可以通过string-name访问它:

class my_decorator:
    def __init__(self, param):
        self.param = param

    def __call__(self, f):
        def f_decorated(instance, c):
            print("decorated with param = %d" % getattr(instance, self.param))
            return f(instance, c)

        return f_decorated


class A:
    def __init__(self, level):
        self.level = level

    @my_decorator(param='level')   # Here is the problematic line!
    def f(self, x):
        return x

if __name__ == "__main__":
    a = A(level=2)
    a.f(1)   # name "self" is not defined