How can a name be "unbound" in Python? What code can cause an `UnboundLocalError`?
Question:
I read the following in the Python documentation:
When a name is not found at all, a NameError
exception is raised. If the current scope is a function scope, and the name refers to a local variable that has not yet been bound to a value at the point where the name is used, an UnboundLocalError
exception is raised. UnboundLocalError
is a subclass of NameError
.
…
Python lacks declarations and allows name binding operations to occur anywhere within a code block.
I don’t understand how this works. If there are no declarations, then when does UnboundLocalError
get raised? How can the variable "not yet be bound" when it is encountered?
See also UnboundLocalError trying to use a variable (supposed to be global) that is (re)assigned (even after first use) for a common problem where a variable expected to be global, is local instead. This question is focused on cases where the programmer expects the variable to be local.
Answers:
You can refer to a name without having assigned to it:
>>> foobar
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'foobar' is not defined
Here foobar
is being referred to, but was never assigned to. This raises a NameError
because the name was never bound.
More subtly, here assignment is not happening because the line that does is never run:
>>> def foo():
... if False:
... spam = 'eggs'
... print(spam)
...
>>> foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in foo
UnboundLocalError: local variable 'spam' referenced before assignment
Because spam = 'eggs'
is never executed, print(spam)
raises an UnboundLocalError
.
Note that nowhere in Python is a name ever declared. You bind or don’t bind, declaration is not part of the language.
Instead, binding is used to determine the scope of a name; binding operations include assignment, names used for a for
loop, function parameters, import statements, name to hold a caught exception in an except
clause, and the name for a context manager in a with
statement.
If a name is bound in a scope (such as in a function) then it is a local name, unless you use a global
statement (or a nonlocal
statement in Python 3) to explicitly mark the name as a global (or a closure) instead.
So the following is an error:
>>> foo = None
>>> def bar():
... if False:
... foo = 'spam'
... print(foo)
...
>>> bar()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in bar
UnboundLocalError: local variable 'foo' referenced before assignment
because foo
is being bound somewhere in the bar
function scope. But if you mark foo
as a global, the function works:
>>> foo = None
>>> def bar():
... global foo
... if False:
... foo = 'spam'
... print(foo)
...
>>> bar()
None
because now the Python compiler knows you wanted foo
to be a global instead.
This is all documented in the Naming and Binding section of the Python reference documentation.
One more place is when the variable is declared in global scope and if it is used in function scope, it should specifically refer as global otherwise UnboundLocalError occurs. Following is the example:
var = 123
def myfunc():
var = var + 1
print var
myfunc()
Error:
UnboundLocalError: local variable 'var' referenced before assignment
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
.
(This 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 var
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.
I read the following in the Python documentation:
When a name is not found at all, a
NameError
exception is raised. If the current scope is a function scope, and the name refers to a local variable that has not yet been bound to a value at the point where the name is used, anUnboundLocalError
exception is raised.UnboundLocalError
is a subclass ofNameError
.
…
Python lacks declarations and allows name binding operations to occur anywhere within a code block.
I don’t understand how this works. If there are no declarations, then when does UnboundLocalError
get raised? How can the variable "not yet be bound" when it is encountered?
See also UnboundLocalError trying to use a variable (supposed to be global) that is (re)assigned (even after first use) for a common problem where a variable expected to be global, is local instead. This question is focused on cases where the programmer expects the variable to be local.
You can refer to a name without having assigned to it:
>>> foobar
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'foobar' is not defined
Here foobar
is being referred to, but was never assigned to. This raises a NameError
because the name was never bound.
More subtly, here assignment is not happening because the line that does is never run:
>>> def foo():
... if False:
... spam = 'eggs'
... print(spam)
...
>>> foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in foo
UnboundLocalError: local variable 'spam' referenced before assignment
Because spam = 'eggs'
is never executed, print(spam)
raises an UnboundLocalError
.
Note that nowhere in Python is a name ever declared. You bind or don’t bind, declaration is not part of the language.
Instead, binding is used to determine the scope of a name; binding operations include assignment, names used for a for
loop, function parameters, import statements, name to hold a caught exception in an except
clause, and the name for a context manager in a with
statement.
If a name is bound in a scope (such as in a function) then it is a local name, unless you use a global
statement (or a nonlocal
statement in Python 3) to explicitly mark the name as a global (or a closure) instead.
So the following is an error:
>>> foo = None
>>> def bar():
... if False:
... foo = 'spam'
... print(foo)
...
>>> bar()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in bar
UnboundLocalError: local variable 'foo' referenced before assignment
because foo
is being bound somewhere in the bar
function scope. But if you mark foo
as a global, the function works:
>>> foo = None
>>> def bar():
... global foo
... if False:
... foo = 'spam'
... print(foo)
...
>>> bar()
None
because now the Python compiler knows you wanted foo
to be a global instead.
This is all documented in the Naming and Binding section of the Python reference documentation.
One more place is when the variable is declared in global scope and if it is used in function scope, it should specifically refer as global otherwise UnboundLocalError occurs. Following is the example:
var = 123
def myfunc():
var = var + 1
print var
myfunc()
Error:
UnboundLocalError: local variable 'var' referenced before assignment
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
.
(This 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 var
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.