Reassigning variable in Python
Question:
In Python, I have an expensive function a(x)
which is currently evaluated multiple times within another function. At the start of that function, I want to evaluate a(x)
once, and reassign the variable name a
locally to that value. But I keep getting an Unbound Local Error.
As a MWE:
def a(x):
return x+1
def main(x):
a = a(x)
return a**2
main(3)
#---------------------------------------------------------------------------
#UnboundLocalError Traceback (most recent call last)
#Input In [62], in <cell line: 8>()
# 5 a = a(x)
# 6 return a**2
#----> 8 main(3)
#
#Input In [62], in main(x)
# 4 def main(x):
#----> 5 a = a(x)
# 6 return a**2
#UnboundLocalError: local variable 'a' referenced before assignment
Obviously, a workaround is to use a line like b = a(x)
instead.
But why is this happening? And how can I reassign the variable name a?
Answers:
The error is occurring because you have already named ‘a’ as a function that you can’t assign as a variable again instead of
a = a(x)
you can name it some other variable like ‘b’ like this
def main(x):
b = a(x)
return b**2
I would disagree with Vatsal’s previous answer. The error happens because the interpreter thinks that you’re trying to call the same a
that you are defining, ie the a
before the equals sign. Though you would probably encounter Vatsal’s error later, as in python, functions are assignable to variables.
The error happens because in this line:
a = a(x)
you are redefining a
to be a local variable. This occurs for all uses for a
within that scope, including the right hand side of the expression. a
doesn’t start off as a global and then become a local at some point during the function’s execution at runtime; if there is an assignment to a
anywhere in the function, then a
is now a local everywhere in that function.
That means that when that line is actually executed, it’s attempting to call the local variable a
before anything has been assigned to it.
Here’s a simpler demonstration of the same effect, where we aren’t changing the type of a
, and we aren’t referencing it on the same line where we’re assigning to it:
>>> a = 42
>>> def foo():
... print(a) # should be 42, right?
... if False:
... a = 42 # should be a no-op, right?
...
>>> foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in foo
UnboundLocalError: local variable 'a' referenced before assignment
In this case, the rebinding of a
happens after it’s used, and it happens in a block that will never even actually be executed when the function is called — but it doesn’t matter, because at the time the function is defined, the existence of that assignment makes a
a local variable.
You can actually see this by inspecting foo
itself, without calling it:
>>> foo.__code__.co_varnames
('a',)
Compare with an implementation that doesn’t make a
into a local:
>>> def foo():
... print(a)
...
>>> foo()
42
>>> foo.__code__.co_varnames
()
Try declaring a
as global variable:
def a(x):
return x+1
def main(x):
global a # <<<<<<<< only this line need to be added.
a = a(x) # Now a is first called (right side) and then reassigned
return a**2
print(main(3))
Now it works!
Output:
16
If you check what is a
now:
print(a)
Output:
4
So global a
has changed. Its no more a function and it cannot be called now:
print(a(3))
Output:
TypeError: 'int' object is not callable
In Python, I have an expensive function a(x)
which is currently evaluated multiple times within another function. At the start of that function, I want to evaluate a(x)
once, and reassign the variable name a
locally to that value. But I keep getting an Unbound Local Error.
As a MWE:
def a(x):
return x+1
def main(x):
a = a(x)
return a**2
main(3)
#---------------------------------------------------------------------------
#UnboundLocalError Traceback (most recent call last)
#Input In [62], in <cell line: 8>()
# 5 a = a(x)
# 6 return a**2
#----> 8 main(3)
#
#Input In [62], in main(x)
# 4 def main(x):
#----> 5 a = a(x)
# 6 return a**2
#UnboundLocalError: local variable 'a' referenced before assignment
Obviously, a workaround is to use a line like b = a(x)
instead.
But why is this happening? And how can I reassign the variable name a?
The error is occurring because you have already named ‘a’ as a function that you can’t assign as a variable again instead of
a = a(x)
you can name it some other variable like ‘b’ like this
def main(x):
b = a(x)
return b**2
I would disagree with Vatsal’s previous answer. The error happens because the interpreter thinks that you’re trying to call the same a
that you are defining, ie the a
before the equals sign. Though you would probably encounter Vatsal’s error later, as in python, functions are assignable to variables.
The error happens because in this line:
a = a(x)
you are redefining a
to be a local variable. This occurs for all uses for a
within that scope, including the right hand side of the expression. a
doesn’t start off as a global and then become a local at some point during the function’s execution at runtime; if there is an assignment to a
anywhere in the function, then a
is now a local everywhere in that function.
That means that when that line is actually executed, it’s attempting to call the local variable a
before anything has been assigned to it.
Here’s a simpler demonstration of the same effect, where we aren’t changing the type of a
, and we aren’t referencing it on the same line where we’re assigning to it:
>>> a = 42
>>> def foo():
... print(a) # should be 42, right?
... if False:
... a = 42 # should be a no-op, right?
...
>>> foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in foo
UnboundLocalError: local variable 'a' referenced before assignment
In this case, the rebinding of a
happens after it’s used, and it happens in a block that will never even actually be executed when the function is called — but it doesn’t matter, because at the time the function is defined, the existence of that assignment makes a
a local variable.
You can actually see this by inspecting foo
itself, without calling it:
>>> foo.__code__.co_varnames
('a',)
Compare with an implementation that doesn’t make a
into a local:
>>> def foo():
... print(a)
...
>>> foo()
42
>>> foo.__code__.co_varnames
()
Try declaring a
as global variable:
def a(x):
return x+1
def main(x):
global a # <<<<<<<< only this line need to be added.
a = a(x) # Now a is first called (right side) and then reassigned
return a**2
print(main(3))
Now it works!
Output:
16
If you check what is a
now:
print(a)
Output:
4
So global a
has changed. Its no more a function and it cannot be called now:
print(a(3))
Output:
TypeError: 'int' object is not callable