When you use a name in a Python program such as variable name function name,etc, Python creates, changes or looks up the name in a namespace. A namespace is the complete list of names that exists in a given context.
There are two types of namespaces, global namespace and local namespace.
The scope of an object determines the locations in the program where it can be accessed, by default, objects are only accessible from within the namespace that they occur. Objects in the global namespace are accessible from anywhere in the program, this are the names that are declared at the top level of a module or a script i.e not inside a function, a class, etc. On the other hand, names declared inside a block such as a function are local to that block and are only accessible within the block.
When we define a function, Python sets up a local namespace for the function. Any object declared inside the function will only be accessible within the function, the object is said to be local to that function. Trying to access local objects outside their scope will raise a NameError
. For example:
def demo():
x = 100
print(x)
demo()
print(x)
Objects inside a function are localized such that they will not crash with objects with similar names outside that function.This allows same name to be used for different objects in different scopes without causing conflicts.
If a function declares a name that also exists in the global namespace, the local name takes precedence over the global name and overwrites it only inside that function. Example:
x = 200
def demo():
x = 100
print(x)
demo()
print(x)
In the above example, there are two distinct variables with a name x
, one in the global namespace with a value of 200
and another in function demo's local namespace with a value of 100
. The value of x
is, therefore, determined by the location where we are using it. Making any changes to the 'x'
inside the function demo does not affect in any way the value of the 'x'
outside its scope.
Name Resolution, The LEGB Rule
When we mention an object by name, the Python interpreter follows an inside-out approach in order to identify the object we are referring to. This approach is often referred to as the LEGB rule, which stands for Local, Enclosing, Global, Built-in. This rule can be summarized as follows:
- Local, first: Look for this name first in the local namespace and use local version if available. If not, go to higher namespace.
- Enclosing, second: If the current function is enclosed within another function, look for the name in that outer function. If not, go to higher namespace.
- Global, third: Look for the name in the objects defined at global namespace
- Built-in,last: Finally, look for the variable among Python’s built-in names.
If the interpreter goes through all the 4 stages and does not locate the name, a NameError
is raised, of course if the operation was not an assignment operation in which case an object with that name will be created in the local scope instead of raising the error.
x = 200
def demo():
x = 300
def inner():
print(x)
inner()
demo()
In the above case, in the call to print x
, the interpreter fails to find an object with a name x
in the function inner
's scope, it moves outward to the enclosing function in which case it finds an 'x'
with a value of 300
thus terminating the search. If still an object with a name 'x'
was not located in the enclosing function 'demo'
, the global x
with a value of 200
would have been returned.
x = 500
def demo():
print(x)
demo()
def demo2():
y = 400
def inner():
print(x + y)
inner()
demo2()
The Global Statement
The global
statement is the only thing that is capable of transforming a name defined inside a function to a global name. This means that the name will behave just like names defined in the global namespace and will be accessible from anywhere in the program.
def demo():
global x
x = 200
demo()
print(x)
If a global name exists similar to the name specified in the global statement, any modifications done to the object it identifies will take effect at a global level.
x = 100
def demo():
global x
x = 500
demo()
print(x)
To declare multiple global names in one global statement, we use the global keyword followed by the comma separated names, example:
def demo():
global x, y, z
x = 4
y = 3
z = x + y
demo()
print(x, y, z)
While the global statement can be useful at times, it can make code mor obscured as it can make it difficult to keep track of the global names. It should, therefore, be avoided whenever possible, after all names inside a function are made local to that function by default because it is the best policy. We can Instead, pass global values as function arguments and then return the modified values.
The nonlocal Statement
The nonlocal
statement is a close cousin of the global
statement. While the global
statement makes a name available at global level, the nonlocal
statement makes a name available to the enclosing function's scope. This also allows the nested function to make changes to variables defined in the enclosing function. Examples:
def demo():
x = 400
def inner():
nonlocal x
x = 600
inner()
print(x)
demo()
def demo2():
y = 0
def inner():
nonlocal y
y = 500
inner()
print(y) #y has been changed
demo2()
Note: We cannot use the nonlocal
statement with a top level function, trying this will raise a SyntaxError
def demo():
nonlocal y
y = 100
demo()