Article
Python Scopes: Global and Nonlocal Variables Explained
What Are Scopes in Python? Understanding Local, Global, and Built-in Variables
In Python, when we write x = "devcuriosity" we create a variable x assigned to the string object - devcuriosity.
We can say that a variable is bound to an object. That variable only exists in parts of our code.
The region of the code where a variable is defined and accessible is known as its lexical scope. Lexical scopes are managed through namespaces, which are mappings between variable names and the objects they reference.
The LEGB Rule: How Python Resolves Variable Names in Different Scopes
Python follows the LEGB rule to resolve variable names:
-
Local Scope: Names defined within a function.
-
Enclosing Scope: Names defined in any enclosing functions.
-
Global Scope: Names defined at the top-level of a module or script.
-
Built-in Scope: Names preassigned in Python, such as
lenorprint.
Built-in Scope
├── Global Scope (Module 1)
│ └── Local Scope (Function 1 in Module 1)
├── Global Scope (Module 2)
│ └── Enclosing Scope (Function 1 in Module 2)
│ └── Local Scope (Inner Function 1 in Function 1 in Module 2)
If you reference a variable name inside scope and Python does not find it in that scope's namespace it will look for it in an enclosing scope's namespace.
That is why IDEs are showing a warning if you use by accident a variable name that belongs to Python built-ins or key-words. For example:
len = "that is a mistake" # Overriding the built-in len() function
my_string = "test"
len(my_string) # Attempting to call the overridden len
Output:
(...) TypeError: 'str' object is not callable
Using len as a variable name, we have overridden the original Python len() function.
If you try to access a variable not in the current namespace, Python will try to
look for it in an outer namespace. If it doesn't find it anywhere, you will receive NameError.
Local Scope in Python: Accessing and Using Local Variables
When we create a variable inside a function, it resides in the function local scope. This means the variable is only accessible within that function and cannot be accessed from outside.
If we call built-in Python scope "2" and our function local scope as a "1". Then we can say that "1" has access to "2". But "2" cannot access "1".
If we consider the built-in Python scope as the outer scope and our function's local scope as the inner scope,
we can say that the inner scope has access to the outer scope.
However, the outer scope cannot access variables from the inner scope.
Take a look at this:
def my_function(name: str) -> str:
greetings = "Hello" # greetings is local to my_function
return f"{greetings} {name.capitalize()}!"
print(my_function("nathan")) # Hello Nathan!
print(greetings) # Raises NameError: name 'greetings' is not defined
Output:
Hello Nathan!
(...) NameError: name 'greetings' is not defined
From the local scope of my_function we could access Python's capitalize() function and greetings variable that we
created in my_function function. But we cannot access greetings outside of my_function.
That is because it exists only in the local scope of my_function.
But remember that Python will "stop" at the first variable that it will find. Consider this example:
greetings = "Welcome"
def my_function(name: str) -> str:
greetings = "Hello"
print(greetings) # Hello
return f"{greetings} {name.capitalize()}!"
print(my_function("nathan")) # Hello Nathan!
print(greetings) # Welcome
Output:
Hello
Hello Nathan!
Welcome
We can say that we have 3 scopes here:
- Python built-in scope (the biggest)
- Our module scope (that exist in our .py file)
- Local scope of our function my_function
The greetings variable on module scope is the Welcome string. And in the local scope of function
my_function the greetings is Hello.
Please notice that Hello didn't override Welcome. We were accessing the same name in two places,
but each time Python "found" different variable bound to that name in a given namespace.
Global Variables in Python: Using the global Keyword
We can change behavior from before with a global keyword.
The global keyword allows a function to modify a variable defined in the global scope.
By declaring a variable as global within a function, you inform Python that you intend to use the globally scoped variable,
not create a new local one.
greetings = "Welcome" # Global scope
def my_function(name: str) -> str:
global greetings
print(greetings) # Outputs: Welcome (global variable)
greetings = "Hello"
print(greetings) # Outputs: Hello (modified global variable)
return f"{greetings} {name.capitalize()}!"
print(my_function("nathan")) # Hello Nathan!
print(greetings) # Outputs: Hello (global variable modified)
Output:
Welcome
Hello
Hello Nathan!
Hello
We modified our previous example, and added the line global greetings. With that, we let Python know
that we want to access greetings from global scope, instead of creating a new variable in my_function local scope.
What Is the nonlocal Keyword in Python? Accessing Enclosing Scopes
By using nonlocal, we tell Python to use or modify a variable from the nearest enclosing scope that is not the global scope. This allows a nested function to modify a variable defined in its enclosing function.
Note that nonlocal cannot be used to access global variables.
Take a look at the example below. I added more prints and comments next to them to make everything more readable.
greetings = "Welcome"
def my_function(name: str) -> str:
greetings = "Hello"
print(greetings) # Hello
def inner_change_greeting():
nonlocal greetings
greetings = "Hi"
inner_change_greeting()
print(greetings) # Hi
return f"{greetings} {name.capitalize()}!"
print(my_function("nathan")) # Hi Nathan!
print(greetings) # Welcome
Output:
Hello
Hi
Hi Nathan!
Welcome
Best Practices for Using Global and Nonlocal Variables in Python
-
Avoid Overusing
global: Modifyingglobalvariables can lead to code that is difficult to read, debug and maintain. Instead, consider returning values from functions and passing them as parameters. -
Use
nonlocalSparingly: Whilenonlocalmight be useful in certain scenarios, excessive use can complicate the scope chain, making the code significantly harder to follow.
Be cautious with mutable types. When dealing with mutable types (like lists or dictionaries), modifications within a function can affect the variable outside the function even without global or nonlocal.
I hope this article was helpful to you! If you have any feedback or questions, please feel free to reach out to me through the "Contact" page.
Thank you for reading!
I hope you found this helpful! If you have any feedback, questions or requests, please reach out to me through Contact page!