def fun():
if False:
x=3
print(locals())
print(x)
fun()
output and error message:
{}
---------------------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
<ipython-input-57-d9deb3063ae1> in <module>()
4 print(locals())
5 print(x)
----> 6 fun()
<ipython-input-57-d9deb3063ae1> in fun()
3 x=3
4 print(locals())
----> 5 print(x)
6 fun()
UnboundLocalError: local variable 'x' referenced before assignment
I am wondering how the python interpreter works. Note that x=3 doesn't run at all, and it shouldn't be treated as a local variable, which means the error would be " name 'x' is not defined". But look into the code and the error message, it isn't the case. Could anybody explain the mechanism principle of the compilation of the python interpreter behind this situation?
Python compile() function takes source code as input and returns a code object which is ready to be executed and which can later be executed by the exec() function. Parameters: Source – It can be a normal string, a byte string, or an AST object. Filename -This is the file from which the code was read.
The compile() function returns the specified source as a code object, ready to be executed.
The Python "UnboundLocalError: Local variable referenced before assignment" occurs when we reference a local variable before assigning a value to it in a function. To solve the error, mark the variable as global in the function definition, e.g. global my_var .
Memory Allocation in Python The methods/method calls and the references are stored in stack memory and all the values objects are stored in a private heap.
So, Python will always categorize each name in each function as being one of local, non-local or global. These name scopes are exclusive; within each function (names in nested functions have their own naming scope), each name can belong to only one of these categories.
When Python compiles this code:
def fun():
if False:
x=3
it will result in an abstract syntax tree as in:
FunctionDef(
name='fun',
args=arguments(...), b
body=[
If(test=NameConstant(value=False),
body=[
Assign(targets=[Name(id='x', ctx=Store())], value=Num(n=3))
],
orelse=[])
]
)
(some stuff omitted for brevity). Now, when this abstract syntax tree is compiled into code, Python will scan over all name nodes. If there is any Name
nodes, with ctx=Store()
, that name is considered to be local to the enclosing FunctionDef
if any, unless overridden with global
(i.e. global x
) or nonlocal
(nonlocal x
) statement within the same function definition.
The ctx=Store()
will occur mainly when the name in question is used on the left-hand side of an assignment, or as the iteration variable in a for
loop.
Now, when Python compiles this to bytecode, the resulting bytecode is
>>> dis.dis(fun)
4 0 LOAD_GLOBAL 0 (print)
3 LOAD_FAST 0 (x)
6 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
9 POP_TOP
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
The optimizer removed the if
statement completely; however since the variable was already marked local to the function, LOAD_FAST
is used for x
, which will result in x
being accessed from local variables, and local variables only. Since x
has not been set, the UnboundLocalError
is thrown. The name print
on the other hand was never assigned to, and thus is considered a global name within this function, so its value is loaded with LOAD_GLOBAL
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With