[CS Fundamentals] Demystifying Variable Scope: Best Practices and Common Pitfalls with Python
Introduction
In computer science, it is so important to fully understand a variable scope as you may face many errors without knowing how they work or your program prints an unexpected output. This article will deeply explain about local, global variables, closure, and keywords used for variable scope handling in python.
Local vs. Global
If a variable is assigned in the most outer, it is the gloabl variable, otherwise, it is local.
Example:
a = 10 # global
def foo():
b = 20 # local
Global
Global variables are used for fixed values, such as pi. Pi is always going to be 3.14…..
Accessing global variables in local is not recommended
- For example, using
global
keyword.
- For example, using
Local
Local variables are declared in loop, function, etc.
They are to solve a certain logic
They no longer exists in the memory after function returns (or finishes)
- But not in closure.
Same Variable Name
b = 20 # global variable
def bar():
b = 30 # local variable
print(b)
bar() # 30
print(b) # 20
In the example, we have two variables with one global and one local scope, and their names are the same. Now, think about why the output of bar()
is 30
. It’s because python seeks for the local variable first. If there is no local variable, then python seeks for the global variable. Therefore, 30
is correct.
Now, think about why the output of print(b)
is 20
. print(b)
is written in the global scope, so python seeks for the global variable. If there is one, it prints the according value. If not, it outputs an error.
Declaring a local variable with the same name of a global variable is called variable shadowing.
UnboundLocalError
Think about the output of foobar()
c = 40
def foobar():
c = c + 10
print('Ex3 >', c)
foobar()
You may think that foobar()
will print 50
;however, the output is UnboundLocalError
.
Traceback (most recent call last):
File "", line 44, in <module>
foobar()
File "", line 38, in foobar
c = c + 10 # UnboundLocalError
UnboundLocalError: local variable 'c' referenced before assignment
You CANNOT modify a global variable in local scope.
We are trying to access a variable that is not even declared. Remember that python will seek for local variable first. In the code above, we are printing c
, so python finds c
in the local scope, but it’s not declared, rather it is doing a caculation.
Global Keyword
If you want to modify it, you can use global
keyword:
c = 40
def foobar():
global c
c = c + 10
print('Ex3 >', c)
foobar()
However, using global
is not recommended as it is not good for code maintenace. It does not mean that global
keyword is wrong. It can be used when needed.
nonlocal Keyword
Before going into the nonlocal
keyword, you must understand closure. You can have a look at my article: [Python] How Closures and Decorators in Python Work Together to Simplify Code
def outer():
e = 70
def inner():
e += 10
print('Ex5 >', e)
return inner
in_test = outer()
in_test()
Traceback (most recent call last):
File "", line 69, in <module>
in_test()
File "", line 64, in inner
e += 10
UnboundLocalError: local variable 'e' referenced before assignment
The error message tells us that e+= 10
got some problems. local variable ‘e’ referenced before assignment
, but it seems like we are using a variable that is already assigned a value. What’s wrong?
Just like I explained before, we are trying to access a variable that is not even declared. Remember that python will seek for local variable first.
We can use nonlocal
keyword, not global
this time because it is using closure.
def outer():
e = 70
def inner():
nonlocal e
e += 10
print('Ex5 >', e)
return inner
in_test = outer()
in_test() # 80
locals() & globals()
locals()
- Python locals() function returns the dictionary of the current local symbol table. We use it to access the local symbol table in Python.
def func(var):
x = 10
def printer():
print('Ex6 >', "Printer Func Inner")
print('Func Inner', locals())
func('Hi')
We can check local declarations easily 😊!
globals()
- In Python, the globals() function is a built-in function that returns a dictionary representing the current global symbol table.
Example:
print(globals())
It returns all the things that are declared in the global scope. Not just variables only, but functions are also there.
As you may see, they are in the dictionary. Which means, a variable is created like this:
test_variable = 100
globals()['test_variable'] = 100
The two lines mean the same thing. We can do something more fun:
for i in range(1, 10):
for k in range(1, 10):
globals()[f'plus_{i}_{k}'] = i + k
print(globals())
print(plus_1_2) # we can call them!