UnboundLocalError
is a special kind of NameError
, that specifically refers to local variables. The same kinds of logical problems that cause NameError
s in code outside of functions, can cause UnboundLocalError
inside them.
Commonly, the problem occurs when trying to write conditional logic that doesn't have any fallback:
import random
def example_if():
if random.random() < 0.5:
result = 1
return result
print(example_if())
This will fail whenever the condition isn't satisfied, because in that case there wasn't anything assigned to result
, so there's no way to use it. It's important to understand here that there are no "default" values for variables, and that assigning None
to something is not the same as deleting the name with del
. You would need to explicitly define a fallback, for example:
def example_if():
result = 1 if random.random() < 0.5 else 0
return result
(This error could also be a symptom of a logical error with indentation: check whether the code using the variable is also supposed to be inside the if
block.)
A sneaky variation on this is a loop that is supposed to assign the value, but runs zero times:
def example_for():
for i in range(0): # this loop will not be entered
result = 1 # therefore this assignment will not happen
return result
print(example_for()))
In more practical examples, a for
loop might fail to run because it is iterating over results from a searching operation (e.g. another loop) that didn't find anything, or because it is iterating over a file that was already read.
UnboundLocalError
can also occur when trying to modify a variable that wasn't already assigned:
def example_increment():
result += 1
example_increment()
This happens because the existing value needs to be looked up before it can be modified, but there is nothing to look up.
Either way, the error will happen even if there is a result
in the global scope, because Python decided ahead of time to treat the variable as local. To solve this, use the global
keyword in addition to giving the initial global value. It must be used in each function that wants to modify the global. (global
in global scope has no effect.)
import random
result = 1
def example_increment():
global result
result += 1
def example_if():
global result
if random.random() < 0.5:
result = 1
return result
example_increment() # result becomes 2
print(example_if()) # may or may not reset to 1
Using global
is not necessary if the value is read-only. However, a global cannot be used to set an initial value for a local of the same name:
result = 1
def does_not_work():
result = result # trying to set a "default" value for a local
if random.random() < 0.5:
result = 2
return result
This can't work either way. As is, UnboundLocalError
will occur. If global result
is added to the function, then result = result
will have no effect, and the function will potentially modify the global (not wanted here). For these cases, the workaround is to simply use a different name for the local.