Python3 Namespaces and Scopes
Namespaces
Here is a passage from the official documentation:
A namespace is a mapping from names to objects. Most namespaces are currently implemented as Python dictionaries.
A namespace is a mapping from names to objects, and most namespaces are implemented using Python dictionaries.
Namespaces provide a way to avoid name conflicts in a project. Each namespace is independent and unrelated, so no duplicates are allowed within a single namespace, but different namespaces can have the same names without any issues.
We can use an example from a computer system: a folder (directory) can contain multiple subfolders, and each subfolder cannot have the same filename, but different subfolders can have files with the same name.
There are generally three types of namespaces:
- Built-in names: Names built into the Python language, such as function names
abs
,char
, and exception namesBaseException
,Exception
, etc. - Global names: Names defined in a module, which record the module's variables, including functions, classes, imported modules, module-level variables, and constants.
- Local names: Names defined in a function, which record the function's variables, including function parameters and locally defined variables. (This also applies to classes.)
Namespace lookup order:
Suppose we want to use the variable tutorialpro
, then Python's lookup order is: Local namespace -> Global namespace -> Built-in namespace.
If the variable tutorialpro
is not found, it will stop the search and raise a NameError
exception:
NameError: name 'tutorialpro' is not defined.
The lifespan of a namespace depends on the scope of the object. If the object's execution is completed, the lifespan of the namespace ends.
Therefore, we cannot access objects from an inner namespace from an outer namespace.
Example
# var1 is a global name
var1 = 5
def some_func():
# var2 is a local name
var2 = 6
def some_inner_func():
# var3 is an enclosed local name
var3 = 7
As shown in the diagram below, the same object name can exist in multiple namespaces.
Scopes
A scope is a textual region of a Python program where a namespace is directly accessible. "Directly accessible" here means that an unqualified reference to a name attempts to find the name in the namespace.
A scope is a textual region of a Python program where a namespace can be directly accessed.
In a Python program, directly accessing a variable will search from the innermost to the outermost scopes until it is found; otherwise, it will raise an undefined error.
In Python, variables are not accessible from every location; their accessibility depends on where they are assigned.
The scope of a variable determines which part of the program can access that specific variable name. There are four types of scopes in Python:
- L (Local): The innermost scope, containing local variables, such as those within a function/method.
- E (Enclosing): Contains non-local and non-global variables. For example, in nested functions, if function A contains function B, the scope of A is nonlocal for B.
- G (Global): The outermost scope of the current script, such as global variables of the current module.
- B (Built-in): Contains built-in variables/keywords, which are searched last.
The order of rules is: L -> E -> G -> B.
If a variable is not found locally, it will be searched in the enclosing scope (e.g., a closure), then globally, and finally in the built-ins.
g_count = 0 # Global scope
def outer():
o_count = 1 # Scope outside the closure function
def inner():
i_count = 2 # Local scope
The built-in scope is implemented through a standard module named builtins
, but this variable name itself is not placed in the built-in scope, so it must be imported to use it. In Python 3.0, you can use the following code to see which predefined variables are available:
>>> import builtins
>>> dir(builtins)
In Python, only modules, classes, and functions introduce new scopes. Other code blocks (such as if/elif/else/, try/except, for/while, etc.) do not introduce new scopes. This means that variables defined within these blocks can be accessed externally, as shown in the following code:
>>> if True:
... msg = 'I am from tutorialpro'
...
>>> msg
'I am from tutorialpro'
>>>
In this example, the variable msg
is defined within an if block, but it can still be accessed outside the block.
If msg
is defined within a function, it becomes a local variable and cannot be accessed externally:
>>> def test():
... msg_inner = 'I am from tutorialpro'
...
>>> msg_inner
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'msg_inner' is not defined
>>>
The error message indicates that msg_inner
is not defined and cannot be used because it is a local variable and can only be used within the function.
Global and Local Variables
Variables defined inside a function have a local scope, while variables defined outside a function have a global scope.
Local variables can only be accessed within the function they are declared, whereas global variables can be accessed throughout the entire program. When a function is called, all variable names declared within the function are added to the scope. Here is an example:
#!/usr/bin/python3
total = 0 # This is a global variable
# Function definition
def sum( arg1, arg2 ):
# Return the sum of the two arguments
total = arg1 + arg2 # total is a local variable here
print ("Inside the function, total is a local variable: ", total)
return total
# Calling the sum function
sum( 10, 20 )
print ("Outside the function, total is a global variable: ", total)
Output of the above example:
Inside the function, total is a local variable: 30
Outside the function, total is a global variable: 0
Global and Nonlocal Keywords
To modify variables in the outer scope from within an inner scope, you need to use the global
and nonlocal
keywords.
The following example modifies a global variable num
:
#!/usr/bin/python3
num = 1
def fun1():
global num # Use the global keyword to declare
print(num)
num = 123
print(num)
fun1()
print(num)
Output of the above example:
1
123
123
To modify variables in the enclosing scope (non-global outer scope), you need to use the nonlocal
keyword, as shown in the following example:
#!/usr/bin/python3
def outer():
num = 10
def inner():
nonlocal num # Use the nonlocal keyword to declare
num = 100
print(num)
inner()
print(num)
outer()
Output of the above example:
100
100
There is a special case where the following code is executed:
#!/usr/bin/python3
a = 10
def test():
a = a + 1
print(a)
test()
The above code will produce the following error:
Traceback (most recent call last):
File "test.py", line 7, in <module>
test()
File "test.py", line 5, in test
a = a + 1
UnboundLocalError: local variable 'a' referenced before assignment
The error message indicates a local scope reference error because a
in the test
function is local, undefined, and cannot be modified.
To modify a
as a global variable:
#!/usr/bin/python3
a = 10
def test():
global a
a = a + 1
print(a)
test()
The output will be:
11
You can also pass the variable as a function parameter:
#!/usr/bin/python3
a = 10
def test(a):
a = a + 1
print(a)
test(a)
Execution output is:
11