global and nonlocal variable in Python
Most of us are already familiar with global variables in Python. If we declare those variables in a module, the functions inside that module (can read python file or .py file) can access the variable. For example, check the code below :
x = 5
def myfnc():
print("inside myfnc", x)
def myfnc2():
print("inside myfnc2", x)
myfnc2()
myfnc()
It will print :
inside myfnc 5
inside myfnc2 5
If you change your code like this :
x = 5
def myfnc():
print("inside myfnc", x)
def myfnc2():
print("inside myfnc2", x)
x = 10
print("x = ", x)
myfnc2()
myfnc()
You will get an error :
File "program.py", line 6, in myfnc2
print("inside myfnc2", x)
UnboundLocalError: local variable 'x' referenced before assignment
The moment you wrote x = 10, Python assume that x is a local variable, and inside the print function, it is giving this error. Because local variables are determined at compile time (from official doc : "local variables are already determined statically"). You can get rid of the error, if you declare x as global.
x = 5
def myfnc():
print("inside myfnc", x)
def myfnc2():
global x
print("inside myfnc2", x)
x = 10
print("x = ", x)
myfnc2()
myfnc()
Now you can run the program again. It won't throw any error. What if we code like this now?
x = 5
def myfnc():
print("inside myfnc", x)
y = 10
def myfnc2():
global x
print("inside myfnc2", x, y)
x = 10
print("x = ", x)
myfnc2()
myfnc()
x = 5
def myfnc():
print("inside myfnc", x)
def myfnc2():
print("inside myfnc2", x)
myfnc2()
myfnc()
It will print :
inside myfnc 5
inside myfnc2 5
If you change your code like this :
x = 5
def myfnc():
print("inside myfnc", x)
def myfnc2():
print("inside myfnc2", x)
x = 10
print("x = ", x)
myfnc2()
myfnc()
You will get an error :
File "program.py", line 6, in myfnc2
print("inside myfnc2", x)
UnboundLocalError: local variable 'x' referenced before assignment
The moment you wrote x = 10, Python assume that x is a local variable, and inside the print function, it is giving this error. Because local variables are determined at compile time (from official doc : "local variables are already determined statically"). You can get rid of the error, if you declare x as global.
x = 5
def myfnc():
print("inside myfnc", x)
def myfnc2():
global x
print("inside myfnc2", x)
x = 10
print("x = ", x)
myfnc2()
myfnc()
Now you can run the program again. It won't throw any error. What if we code like this now?
x = 5
def myfnc():
print("inside myfnc", x)
y = 10
def myfnc2():
global x
print("inside myfnc2", x, y)
x = 10
print("x = ", x)
myfnc2()
myfnc()
If you run the program, you will see desired output. But now, if you want to write to y inside myfnc2() (for example, assign something like y = 1), you can't use global y, as y is not a global variable. You can try this following code that fails successfully :
x = 5
def myfnc():
print("inside myfnc", x)
y = 10
def myfnc2():
global x
global y
print("inside myfnc2", x, y)
x = 10
print("x = ", x)
y = 1
print("y = ", y)
myfnc2()
myfnc()
You will get this error : NameError: name 'y' is not defined
We need to understand that, y is not a global variable. Here nonlocal can help! Instead of global y, just write nonlocal. It will make y available and writable inside myfnc2().
x = 5
def myfnc():
print("inside myfnc", x)
y = 10
def myfnc2():
global x
nonlocal y
print("inside myfnc2", x, y)
x = 10
print("x = ", x)
y = 1
print("y = ", y)
myfnc2()
myfnc()
This is something I learnt today. :)
Comments
- `global`: Names listed in a global statement must not be defined as formal parameters or in a for loop control target, class definition, function definition, import statement, or variable annotation.
- `nonlocal`: Names listed in a nonlocal statement, unlike those listed in a global statement, must refer to pre-existing bindings in an enclosing scope ...
In this case, `x` is not considered within an enclosing scope, so it can be accessed only with `global x`. Likewise, `y` is within a function definition so must be `nonlocal y`. I would have thought that the enclosing scope would have found `x` in the global environment, so I'll need to brush up on why `nonlocal x` fails (with `SyntaxError: no binding for nonlocal 'x' found`).