Python装饰器变量得分

Mih*_*gea 1 python closures scope decorator

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

这是代码:

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()
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

任何人都可以解释为什么取消注释这两行代码会破坏它吗?

Sil*_*Ray 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
Run Code Online (Sandbox Code Playgroud)

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