Consider the following python snippet (I am running Python 3)
name = "Sammy"
def greet():
name = 'johny'
def hello():
print('hello ' + name) # gets 'name' from the enclosing 'greet'
hello()
greet()
This produces the output hello johny
as expected
However,
x = 50
def func1():
x = 20
def func2():
print("x is ", x) # Generates error here
x = 2
print("Changed the local x to ",x)
func2()
func1()
print("x is still ",x)
generates an UnboundLocalError: local variable 'x' referenced before assignment
.
Why does the first snippet work, whereas the second doesn't?
The error is actually caused (indirectly) by the following line, i.e. x = 2
. Try commenting out that line and you'll see that the function works.
The fact that there is an assignment to a variable named x
makes x
local to the function at compile time, however, at execution time the first reference to x
fails because, at the time that the print()
statement is executed, it does not exist yet in the current scope.
Correct it by using nonlocal
in func2()
:
def func2():
nonlocal x
print("x is ", x)
x = 2
print("Changed the local x to ",x)
The reason that the first function (greet()
) works is because it's OK to read variables in an outer scope, however, you can not assign to them unless you specify that the variable exists in an outer scope (with nonlocal
or global
).
You can assign to a variable of the same name, however, that would create a new local variable, not the variable in the outer scope. So this also works:
def func1():
x = 20
def func2():
x = 2
print('Inner x is', x)
func2()
print('Outer x is', x)
Here x
is assigned to before being referenced. This creates a new variable named x
in the scope of function func2()
which shadows the x
defined in func1()
.
In a given scope, you can only reference a variable's name from one given scope. Your variable cannot be global at some point and local later on or vice versa.
For that reason, if x
is ever to be declared in a scope, Python will assume that you are refering to the local variable everywhere in that scope, unless you explicitly state otherwise.
This is why you first function, greet
, works. The variable name
is unambiguously coming from the closure. Although, in func2
the variable x
is used in the scope and thus you cannot reference the x
from the closure unless explicitly stating otherwise with nonlocal
.
The following errors might enlighten us on this.
def func1():
x = 20
def func2():
print("x is ", x)
global x
print("Changed the local x to ",x)
func2()
This raises a SyntaxError: name 'x' is used prior to global declaration
. This means that the closure's x
cannot be used and then the global one.
Here is another case using global
at the top of func2
.
def func1():
x = 20
def func2():
global x
print("x is ", x)
x = 2
print("Changed the local x to ",x)
func2()
This code was exectued without error, but notice that the assignment to x
updated the global variable, it did not make x
become local again.
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