1

我创建了一个简单的装饰器,它接收一个参数(使用函数而不是一个类),当发生一些奇怪的事情时:添加一行代码会中断前一行的执行。

这是代码:

def my_decorator(sublabel):
    def duration_wrapper(f):

        print sublabel
        # Uncommenting this code will break the previous line - why?
        # if sublabel is None:
        #     sublabel = f.func_name

        def wrapped_function(*args, **kwargs):
            return f(*args, **kwargs)

        return wrapped_function

    return duration_wrapper

@my_decorator('me')
def myf(): pass

myf()

取消注释这些代码行会导致此异常:

Traceback (most recent call last):
  File "test.py", line 16, in <module>
    @my_decorator('me')
  File "test.py", line 4, in duration_wrapper
    print sublabel
UnboundLocalError: local variable 'sublabel' referenced before assignment

谁能解释为什么取消注释那两行代码会破坏它?

4

2 回答 2

7

装饰器是闭包,并且从封闭范围引用的封闭范围中的所有标签必须在封闭范围内保持静态。如果你想让一个封闭范围内的变量在封闭范围内是可变的,你需要将它包装在一个可变对象中,例如:

def my_decorator(sublabel):
    sublabel = [sublabel]
    def duration_wrapper(f):

        print sublabel[0]
        if sublabel[0] is None:
            sublabel[0] = f.func_name

        def wrapped_function(*args, **kwargs):
            return f(*args, **kwargs)

        return wrapped_function

    return duration_wrapper

正如@Bakuriu 在评论中指出的那样,Python 3 引入nonlocal了删除这个限制,您也可以创建sublabel一个全局变量来解决这个问题,但是全局变量通常不是一个好主意。

于 2012-12-06T15:54:43.270 回答
2

您的问题可以简化为这个简单的示例

sublevel = 1

def func():
    print sublevel
    sublevel = 2

func()

问题是当您在内部范围内分配一个变量时,您正在屏蔽外部范围(全局级别或父函数级别范围),阅读文档以了解python 范围规则如何工作,需要注意的重要一点是scopes are determined statically

因此,在我的示例中,您需要global sublevel在调用 print 之前说,您需要使用nonlocal关键字(python 3+)

于 2012-12-06T16:03:16.397 回答