Why does this UnboundLocalError occur (closure)?
Python doesn't have variable declarations, so it has to figure out the scope of variables itself. It does so by a simple rule: If there is an assignment to a variable inside a function, that variable is considered local.[1] Thus, the line
counter += 1
implicitly makes counter
local to increment()
. Trying to execute this line, though, will try to read the value of the local variable counter
before it is assigned, resulting in an UnboundLocalError
.[2]
If counter
is a global variable, the global
keyword will help. If increment()
is a local function and counter
a local variable, you can use nonlocal
in Python 3.x.
UnboundLocalError: local variable referenced before assignment in python closure
You have overwritten the variable with filename = os.path.join(os.getcwd(), filename)
, if you change the filename =
to something other than filename
you won't get a local variable 'filename' referenced before assignment
error.
Once you set filename =
you are no longer referring to the parameter filename
that is passed in you are referring to the local filename
in the scope of the inner function which you try to use in the if before you have it defined.
You will have the same problem with dest, if you change the two lines and the other variables to something like:
filename_ = os.path.join(os.getcwd(), filename)
dest_ = os.path.join(dest, filename)
You will see the code runs fine as filename now refers to the parameter not to a local variable defined in your inner function.
You will see the exact same behaviour if you try to reassign x
in your first function and try to access x
before you have defined it:
def makeInc(x, y):
def inc():
print y + x # will cause referenced before assignment error
x = 5 # now x is local to the inner func, the x from the outer function is overridden
return y + x
return inc
If you print the __closure__
attribute you will see what happens:
def makeInc(x, y):
def inc():
return y + x
return inc
inc5 = makeInc(5, 10)
inc10 = makeInc(10, 5)
print(inc5.__closure__)
(<cell at 0x7f180df67e50: int object at 0xef00f8>, <cell at 0x7f180df67fa0: int object at 0xef0080>)
Now reassigning x:
def makeInc(x, y):
def inc():
print y + x
x= 5
return y + x
return inc
inc5 = makeInc(5, 10)
inc10 = makeInc(10, 5)
print(inc5.__closure__)
(<cell at 0x7fea11889fd8: int object at 0x291e080>,)
After reassigning in the inner function, there is no longer a reference to x
.
So basically the fundamental difference between your two original functions is that in one you are reassigning the variable in the local scope and in the other you are not. As you can see from the code above if you do something similar in the first function the outcome is exactly the same.
There is a nice tut here on scopes LEGB etc..
Why I encounter an UnboundLocalError when I wrote a python closure, but in another similar code snippet I didn't?
The assignment x = temp
creates a new local variable that shadows the non-local x
defined by wrapper
. Even though temp = x + 1
precedes it at run time, it still refers to the local variable x
that isn't yet initialized.
UnboundLocalError: local variable 'actionNumber' referenced before assignment and Array
Since you are defining a function, it is treating any reference to actionNumber as a local variable, a variable that exists only within the function with no connection to the global variable actionNumber.
You can add global actionNumber
as the first line to your function, which will make any reference to actionNumber in the function, a reference to the global actionNumber, not a local one. However, I suggest you define actionNumber as 0 in the function unless you need to use actionNumber in multiple difference functions/outside the function.
So, you're fixed code would be either:
actionNumber = 0
dataX = []
dataY = []
f = open("track_cursor_position_log.txt", "r")
#print(f.read())
def mainLoop():
#I need to store the first line of the file into dataX, then the second into dataY, then the 3rd into dataX, and so on...
global actionNumber
actionNumber = actionNumber + 1
mainLoop()
mainLoop()
or
dataX = []
dataY = []
f = open("track_cursor_position_log.txt", "r")
#print(f.read())
def mainLoop():
#I need to store the first line of the file into dataX, then the second into dataY, then the 3rd into dataX, and so on...
actionNumber = 0
actionNumber = actionNumber + 1
mainLoop()
mainLoop()
Also, as an aside, actionNumber += 1
would be the same as actionNumber = actionNumber + 1
UnboundLocalError on local variable when reassigned after first use
Python treats variables in functions differently depending on whether you assign values to them from inside or outside the function. If a variable is assigned within a function, it is treated by default as a local variable. Therefore, when you uncomment the line, you are trying to reference the local variable c
before any value has been assigned to it.
If you want the variable c
to refer to the global c = 3
assigned before the function, put
global c
as the first line of the function.
As for python 3, there is now
nonlocal c
that you can use to refer to the nearest enclosing function scope that has a c
variable.
Related Topics
What Are Iterator, Iterable, and Iteration
How Are Iloc and Loc Different
How to Watch a File For Changes
How to Get File Creation and Modification Date/Times
How to Do a Case-Insensitive String Comparison
What's With the Integer Cache Maintained by the Interpreter
Fastest Way to Check If a Value Exists in a List
Cannot Kill Python Script With Ctrl-C
How to Replace Multiple Substrings of a String
How to Install Packages Offline
How to Sort a Dictionary by Key
Difference Between Shallow Copy, Deepcopy and Normal Assignment Operation
Fatal Error: Python.H: No Such File or Directory
How to Use Multiprocessing Pool.Map With Multiple Arguments
How to Get a Cron Like Scheduler in Python