While trying to implement a decorator with a closure I ran into a somewhat strange behavior, where a variable can be read, but if try to assign it later, it becomes undefined even before the assignment.
def run_once(fn):
to_run = True
def decorated(*args, **kwargs):
if to_run:
print(to_run) #this access works
#to_run = False #while this doesn't
return fn(*args, **kwargs)
return decorated
class A:
def __init__(self):
self.c=0
@run_once
def method1(self):
self.c =1
print(f"Ran {self.c} times")
a=A()
a.method1()
a.method1()
a.method1()
The code above runs.
but if to_run = False
is uncommented, then it fails with UnboundLocalError: local variable 'to_run' referenced before assignment
I find it really strange that I can read it, but if I try to assign it in the body of the if, it fails where it didn't before.
Am I missing some obvious scoping rule?
CodePudding user response:
Since you're trying to modify a variable from the outer scope, you need to tell python that you are trying to access the variable from the outer scope:
def run_once(fn):
to_run = True
def decorated(*args, **kwargs):
nonlocal to_run # use the variable from outer scope
if to_run:
print(to_run)
to_run = False # set the flag in the outer scope
return fn(*args, **kwargs)
return decorated
Result:
True
Ran 1 times