Understanding the namespaces, scopes, and behavior of variables in Python functions is crucial for writing efficiently and avoiding runtime errors or exceptions. In this article, we’ll delve into various aspects of namespaces and Python variable scopes and learn how Python manages local, global, and enclosing variables in detail.
We already discussed Python functions in detail, which can be found here. Python uses abstraction principles to hide complex logic and expose only the necessary outputs, while decomposition creates modular, readable, and reusable functions.
These principles are obvious enough to understand how Python handles its variable scopes for a function definition and nested functions, which we will explore through detailed examples. By the end of this article, you should clearly understand these concepts and how to apply them effectively in your programs.
Variables in Python are containers that store data or values (such as int, float, str, bool, etc.). The memory location where a variable is stored and also accessible for future use is called the scope of a variable.
There are two types of variables in Python, namely:
Also read: Mutable vs Immutable Objects in Python
Python namespace is a space or dictionary that holds identifiers (commonly called variable names) as its keys and their respective objects as the values in the memory space. Python programming language has 4 types of Namespaces, namely:
We’ll soon look at different examples to better understand this concept. But before that, it is really important to understand the variable scopes mentioned above.
In Python, scope refers to a program’s area or textual region where the variables are directly accessible. At any time during execution, there are :
Note: You create these user-defined scopes in your program to run it efficiently. However, Python’s Built-in Variables also have a scope known as Built-in Scope.
Now, you have a basic understanding of namespaces and variable scope. Let’s dive deeper to understand how scoping rules are applied in Python Programming Language. There’s a common abbreviation, LEGB Rule, which stands for Local, Enclosing, Global, and Built-in.
LEGB Rule states that the interpreter can search for an identifier from the inside out, meaning it starts by looking for a variable name or namespace in the local scope first. If the namespace is not present there, it will move towards the enclosing scope of your program. ext, it checks the global scope to find the namespace. Finally, if the identifier is still not found, the interpreter looks at the built-in scope provided by Python.
Furthermore, if the interpreter doesn’t find the name in any of these locations, then Python raises a `NameError` exception, meaning the variable is not defined in the program.
Also, it is really important to remember that you’ll have to move upward in the hierarchy of the LEGB Rule from the current scope.
Also read: Comprehensive Guide to Advanced Python Programming
Now, let’s go one-by-one through all these examples to understand all these concepts in depth:
To understand this let’s take an example, here the function `g(y)` not only prints the global variable `x` but also modifies it as `x+1`.
Now, since `x` is not defined within `g(y)`, Python fetches the value of global variable `x`.
def g(y):
print(x)
print(x+1)
# Since x is not in local scope it will go and fetch the value of x from global variable
x = 1
g(x) # Global Inside Local Variable
print(x) # Global Variable
1
2
1
The output shows the value of `x` and `x+1` confirming that the global variable `x` remains unchanged, but has been used by the local scope for it to output the results properly.
Now, look at this example, here we have a function definition `g(y)` and inside below given function `g`, name `x` is defined as a local variable and also modified.
def g(y):
x = 10 # Local variable
x += 1
print(x)
x = 1 # Global Variable
g(x)
print(x)
11
1
As evidence, the global `x` remains unchanged, and the local variable used its local scope variable to print the statements showing 11 as output through the function and 1 output by the global scope, as expected.
Also read: Comprehensive Guide to Python Built-in Data Structures
But is it possible to modify the global variable `x` without declaring it as `global`?
The answer is no! You cannot modify any global variable value from the local scope, as doing so will result in an error.
def h(y):
# Function can use global variable, if it doesn't have any
x += 10 # But cannot change the global value inside the local variable
x = 1
h(x)
print(x)
UnboundLocalError Traceback (most recent call last)
<ipython-input-3-130c677cc9ab> in <cell line: 5>()
3
4 x=1
----> 5 h(x)
6 print(x)
<ipython-input-3-130c677cc9ab> in h(y)
1def h(y):
----> 2 x+=10
3
4 x=1
5 h(x)
UnboundLocalError: local variable `x` referenced before assignment
This results in an `UnboundLocalError` because Python treats `x` as a local variable due to the assignment operation, but it hasn’t been initialized locally. Also, though local variables can access global variables, you cannot make changes to the global variable (you can only read, not write).
Also read: Fundamentals of Python Programming for Beginners
But since I have always told you that Python is actually a sweet language and even though it isn’t recommended to do any modification or changes on the global variable. That doesn’t mean Python doesn’t give you this functionality, as by declaring `x` as `global` using the same keyword, the function can modify the global variable `x`.
def h(y):
global x # Now it can change the global value inside the local variable
# But that isn't a good way of coding, you should focus on reducing this global keyword usage
x += 10
x = 1
h(x)
print(x)
11
The output confirms that `x` has been updated globally. However, remember that the changes will affect the entire program, as modifying the main function will also affect other functions, which isn’t good programming practice.
Also, you can modify the global variable inside the function `g(x)` by incrementing `x` by 10. It’ll print the new value and return it.
Note: This doesn’t mean that you are modifying the global variable itself, as it, anyway, isn’t possible without the `global` keyword.
def g(x):
x += 10
print("in f(x): x =" , x)
return x # Returning f(x)
x = 2
z = g(x)
print("in main program scope: z =", z)
print("in main program scope: x =", x)
in f(x): x = 12
in main program scope: z = 12
in main program scope: x = 2
Here, the global `x` remains unchanged, while the returned value `z` is the new updated value.
The functions that are defined inside another `def` function are called nested functions or inner functions.
Here is an example for a nested function for a better understanding:
def f():
def g():
print("Inside function g")
g()
print("Inside function f")
f()
Inside function g
Inside function f
Note: The nested function `g` is called within the function `f`, printing messages from both functions. Calling function `g` outside the `f` will results in an error, since `g` is not defined in the global scope.
g() # This function is not defined outside the function f
TypeError Traceback (most recent call last)
<ipython-input-8-5fd69ddb5074> in <cell line: 1>()
----> 1 g()
TypeError: g() missing 1 required positional argument: 'x'
Python offers a different and special variable scope to only the names that are defined inside the nested function, known as an Enclosing Scope. It is also known as the `non-local` scope. Enclosing scope is the scope of the outer function when there is a local function, which is an inner or nested function.
def f():
x = 1
def g():
print("Inside function g")
print(x)
g()
print("Inside function f")
f()
This variable `x` is present inside the enclosing scope, which you can also use in local scope, as shown in above example. Here’s it output:
Inside function g
1
Inside function f
Now, let’s move ahead and understand this new scope better.
Again, modifying the global variable `x` inside the nested function is impossible.
def g(x):
def h():
x += 1
print('in h(x): x =', x)
x = x + 1
print('in g(x): x =', x)
h(x)
return x
x = 3
z = g(x)
print('in main program scope: x =', x)
print('in main program scope: z =', z)
in g(x): x = 4
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-12-5bcfb2edb396> in <cell line: 11>()
9
10 x=3
---> 11 z=g(x)
12 print('in main program scope: x =',x)
13 print('in main program scope: z =',z)
<ipython-input-12-5bcfb2edb396> in g(x)
5 x=x+1
6 print('in g(x): x =',x)
----> 7 h(x)
8 return x
9
TypeError: g.<locals>.h() takes 0 positional arguments but 1 was given
As the function `h()`, is defined without any parameters, but `h(x)` is called with an argument. This will give a `TypeError`. Also, though the enclosing variable can access the global variable, you cannot perform changes in the global variable.
Similar, to as the `global` keyword, python offers its developers with a `nonlocal` keyword. That allows the nested function `h` to modify the variable `x` defined in the enclosing function `g`.
def g(x):
def h():
nonlocal x # Tell h() to use x from g(x)
x += 1
print('in h(x): x =', x)
x = x + 1
print('in g(x): x =', x)
h() # Call h() without any arguments
return x
x = 3
z = g(x)
print('in main program scope: x =', x)
print('in main program scope: z =', z)
in g(x): x = 4
in h(x): x = 5
in main program scope: x = 3
in main program scope: z = 5
The outputs show the changes made within both functions and that the global variable `x` remains unchanged.
Lastly, note that depending upon where the scopes are defined, each scope corresponds to different levels of access throughout the program and will have different lifespans for namespace/s within the code.
Also read: A Complete Python Tutorial to Learn Data Science from Scratch
This article explored how Python handles local and global variables and nested functions. We have learned that a namespace is a dictionary that Python offers developers, from which you can find a variable name and its value stored in the scope of Python memory. Further, the Scopes are of four types: local, enclosing, global, and built-in.
These are really useful for avoiding naming conflicts and for keeping track of which names/identifiers refer to which objects throughout the program’s different parts.
Also, if you want to modify a variable in the global scope from the local scope, you can use the `global` keyword. Similarly, you can use the `nonlocal` keyword to close the scope.
I hope this has helped you gain insights into writing good production-level codes while following industry-related best practices and reducing developer-defined exceptions. However, this is the first step towards making our program more robust, and we have much more to cover.
So, stay tuned for the next article, where we’ll discuss File Serialization and Deserialization in the Python Programming Language!
Ans. Namespaces in Python organize and manage the names or identifiers in a program. Basically, they act like containers or dictionaries that store names mapped to their objects, such as variables and functions.
Ans. The LEGB rule in Python is the order in which a Python Interpreter looks up while working with the names or commonly known as identifiers. It stands for Local, Enclosing, Global, and Built-in:
1. Local: Names defined within a function.
2. Enclosing: Names in the local scope of any enclosing function (nested function).
3. Global: Names defined at the top level of a script or module.
Built-in: Names that are pre-defined in Python, such as `print` or `len`.
Ans. Global keyword allows a function to modify a variable defined in the global scope and enables the variables to reside outside of the operation. Note: Just because you can do it, doesn’t mean you should use this (often), because it is not a good programming practice.
Ans. Overuse of global variables can lead to programs that are difficult to understand and maintain. It can also cause unintended changes, making debugging more difficult. It is generally better to use local variables and pass them as needed.
Ans. Similar to global keywords, Python offers `nonlocal` keywords to modify enclosing variables. The non-local keywords can modify variables defined in the enclosing function of a nested function, providing a way to control variable scope in nested functions.