I created a simple decorator that received an argument (using functions instead of a class), when something strage happened: adding a line a code breaks the execution of a previous line.
Here’s the code:
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()
Uncommenting those lines of code cause this exception:
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
Can anyone explain why uncommenting those 2 lines of code break it?
Decorators are closures, and all labels from an enclosing scope referenced from an enclosed scope must remain static within the enclosed scope. If you want to have a variable from an enclosing scope be mutable from an enclosed scope, you need to wrap it in a mutable object, for example:
As @Bakuriu notes in the comments, Python 3 introduces
nonlocalto remove this restriction, and you could also makesublabela global to get around the problem, but globals are usually a bad idea generally.