Python 类外部的装饰器方法

-4 投票
2 回答
2459 浏览
提问于 2025-04-17 19:33

你好,我想让默认的调用显示“nate在课堂上”。

我希望能把一个类的方法包裹起来,设置一个默认值,这样开发者就不需要再设置这个默认值了。

但是下面的代码给我报了错:

Traceback (most recent call last):
File "", line 18, in 
File "", line 9, in rtn
UnboundLocalError: local variable 'var1' referenced before assignment    

我已经尝试了大约一个小时,但就是无法让它正常工作。

这是代码:

class Bob(object):

    def bob(self,var1='bob',var2=' is in the class'):
    print var1,var2


def defalter(func):
    def rtn(self=None,*args, **kwargs):
        if not var1:
            var1 = 'nate'  
    return rtn


b = Bob()

r = defalter(b.bob) 
r()

2 个回答

1

你有几个误解。首先,你的装饰器是在被装饰的函数之前被调用的,所以它无法访问那个函数内部的局部变量。其次,即使它能访问这些变量,给它们赋新值也不会改变其他地方的变量。第三,装饰器需要负责调用底层的函数,如果它想这么做的话。你的装饰器从来没有调用底层的函数,所以它无法利用这个函数的行为。

这里有一种方法可以做到:

class Bob(object):
    def bob(self,var1='bob',var2=' is in the class'):
        print var1,var2

def defalter(func):
    def rtn(var1='nate', *args, **kwargs):
        func(var1, *args, **kwargs)
    return rtn

>>> r = defalter(b.bob)
>>> r()
nate  is in the class

我不太明白你为什么要在后面显式地进行装饰,而不是直接在类里面装饰这个方法。而且,如果用户给r传递参数,你希望发生什么也不太清楚。我做的方式基本上就是把var1的默认值替换成了“nate”。

1

这听起来像个坏主意,但下面的代码可以工作:

class Bob(object):

    def bob(self,var1='bob',var2=' is in the class'):
        print var1,var2


def defalter(func):
    func.im_func.func_defaults = ('nate',) + func.im_func.func_defaults[1:]
    return func


b = Bob()

r = defalter(b.bob) 
r()

我很确定在python3.x中,属性名称是有变化的,不过我现在没什么动力去查具体改成什么了;如果你知道的话,可以随意编辑一下。

需要注意的是,这样做会改变所有的Bob实例,而不仅仅是b这个。如果你想要的效果不一定是这样的话,就要考虑清楚了……

如果你不想改变所有的Bob实例,可以使用下面这个:

def defalter(func):
    def new_func(var1='nate',**kwargs):
        return func(var1=var1,**kwargs)

    return new_func

撰写回答