This content originally appeared on Level Up Coding - Medium and was authored by Nikita Prasad
Everything You Need to Know to Optimize Your Codes!
If you’re an aspiring Python Developer or Data Scientist, understanding how scopes work is one of the most important concepts.
It’ll not only help you to better understand every program you write but also makes debugging a lot easier when you face any unexpected output value/s, eventually leading you to write good production-level codes.
Bonus: Want to become a key part of the Generative AI revolution? Read about the trending Technology behind it!, here.👈🏻
Let’s get started with,
What are the Namespaces?
Programmatically speaking, namespace is a space or dictionary that holds identifiers (or variable names) as the keys and their respective objects as the values.
There are 4 types of namespaces in Python programming language, namely:
- Built-in Namespace
- Global Namespace
- Enclosing Namespace
- Local Namespace
Later, we’ll look at several examples to better understand these concepts. But before that, let’s understand the scopes.
What is the Scope of a Namespace?
Simply stating, scope is a textual region or code block of a program where a namespace is directly accessible.
Note: Not all variables are accessible throughout the program. Some variables are created and others get deleted during the runtime.
What is the LEGB Rule?
There’s a common abbreviation for understanding the scoping rules in Python, called LEGB stands for Local, Enclosing, Global and Built-in.
According to this rule, the interpreter searches for an identifier from the inside-out. It starts by looking in the local scope; if the namespace is not there, it moves to enclosing scope. If it’s not found, it checks the global scope. If the identifier is still not found, the interpreter finally looks in built-in scope.
If the interpreter doesn’t find the name in any of these locations, Python raises a NameError exception.
Till here if you find this read helpful, do not forget to clap 👏🏻 and comment🖋️ down your thoughts!
Now, to begin with, let’s first understand local and global scopes, as these are probably the most commonly used and often confused.
A. Local and Global Namespaces
— What is the `local` and `global` scope?
Since the identifier a is the part of main function, it is called a global variable and is inside the global scope, whereas clearly variable b is inside the local scope.
# `a` is inside a global scope, hence called global variable
a = 2
def temp():
# `b` is inside temp's local scope, hence called local variable
b = 3
print(f"this is local scope {b}") # Output: this is local scope 3
temp()
print(f"this is global scope {a}") # Output: this is local scope 2
So far, simple and clear.
— Can `local` and `global` scopes have same names?
Now, if you’re as curious as me👩🏻💻, you may ask, “What if the two identifier have same name?”
So, nothing. Python give weightage to scopes and not the identifiers’ names. Even if two variables have the same names, they are in different scopes. Hence, Python has no objections.
# `a` is inside a global scope, hence called global variable
a = 2
def temp():
# `a` is inside a local scope, and hence called local variable
a = 3
print(f"this is local scope {a}") # Output: this is local scope 3
temp()
print(f"this is global scope {a}") # Output: this is local scope 2
But as I have always recommend, you don’t write programs for computers but for your team members, so using proper naming convention is a good programming practice.
— What if you don’t have a `local` scope?
Based on above theory that prove it, herein, this example👇
# `a` is inside a global scope, hence called global variable
a = 2
def temp():
# `a` is inside a local scope, and can access the global variable
print(f"this is local scope {a}") # Output: this is local scope 2
temp()
print(f"this is global scope {a}") # Output: this is global scope 2
Note: A local scope can access global variables but, the vice-versa is not true, as per the LEGB Rule Hierarchy.
— Can you modify the 'global` variable from the `local` scope?
Though local name/s can access global name/s, but you cannot perform changes in the global name (you can only read, not write).
For instance, if you try to do so like in this code snippet, Python will immediately throw an UnboundLocalError exception because it treats `x` as a local variable due to the assignment operation, but it hasn’t been initialized locally.
# `a` is inside a global scope, hence called global variable
a = 2
def temp():
# attempting to modifying the global variable a within local scope, but it will cause an error
a += 1 # UnboundLocalError because a is referenced before assignment
print(f"this is local scope {a}")
temp()
print(f"this is global scope {a}")
# Output: UnboundLocalError: cannot access local variable 'a' where
# it is not associated with a value
But since Python is such a friendly-language, it gives you a functionality to modify global name using global keyword.
# `a` is inside a global scope, hence called global variable
a = 2
def temp():
# modifying global variable `a` by declaring the global keyword
global a
a = 1
print(f"this is local scope {a}") # Output: this is local scope 1
temp()
# global variable a has been modified to `1`
print(f"this is global scope {a}") # Output: this is global scope 1
Note: Just because you can do it, doesn’t mean you should use this (often), because it is not a good programming practice. There might be the chances that other functions also depends on this name and if you make changes to the global variable from one function, then other function may get the same updated global name, resulting in wrong or biased output.
— Can you create a global variable inside the local scope?
Alright, this case can be interesting and confusing at the same time. What if I’ create a namespace inside the function? What’s your guess: will this be a global or a local variable?
def temp():
# even though `a` is inside temp's local scope, but since it has `global` keyword, hence it is called global variable
global a
a = 1
print(f"this is local scope {a}") # Output: this is local scope 1
temp()
print(f"this is global scope {a}") # Output: this is global scope 1
Since, I used the global keyword, the function name has been created in the global scope.
Note: This example is just for demonstration purposes — you can do this, but that doesn’t means it’s a pythonic way to create a global variable.
— What is the scope of the function parameter?
Again, what if I ask you, in the function below, whether the parameter z is a local variable or a global variable?
Alright, if your answer is, local variable, then you’re correct!
Always remember, a parameter to the function is always a local variable, meaning it is created in a local scope.
# `a` is inside a global scope, hence called global variable
a = 2
def temp(z):
# `z` is a local variable in temp's scope
print(f"this is local scope {z}") # Output: this is local scope 2
# passing 2 as an argument
temp(2)
# global variable a remains unchanged
print(f"this is global scope {a}") # Output: this is global scope 2
You can also prove my statement by printing z and Python will throw a NameError exception, stating that z is not defined independently.
B. Built-in Namespace
Okay, now let’s just look at the built-in scope.
Technically, built-in namespace is pretty easy to understand because they’re just the names that have been already assigned to the Python.
For instance, print is the built-in function in Python.
# this is a built-in function
print("""Hello, you're learning Python scopes with
Nikita Prasad""")
Not only this, you can look at the complete list of built-in scopes by importing the builtins module.
# built-in scope
import builtins
print(dir(builtins))
Note: dir and print both are the built-in scopes.
Also, max is also a built-in function in Python that finds the largest value of a list iterable.
# l is the part of global scope
l = [1,2,3]
# max is the part of built-in scope
print(max(l)) # Output: 3
— Can you rename the `built-in` namespaces?
Be careful and never use the built-in function variable as a global variable because if you do so, it will override the original function, and you will not be able to use the actual functionality of the function, again.
Let’s understand this using two examples:
Example 1: Here when max is treated as a local variable, as Python uses the LEGB rule, the built-in max is overridden by this local variable.
# l is the part of global scope
l = [1,2,3]
def temp():
# `max` is part of the local scope and overrides the built-in scope
max = 1
# TypeError because max is not callable
max(l) # Output: TypeError: 'int' object is not callable
temp()
Example 2: Alternatively, in this snippet when max is treated as a global variable, the built-in max is overridden by this global variable.
# l is the part of global scope
l = [1,2,3]
# Built-in `max` overridden by the global scope `max`
max = 1
# TypeError because max is not callable
max(l) # Output: TypeError: 'int' object is not callable
This implies that the built-in function can be renamed.
Note: To prevent such unintended conflicts, it is advisable to not use python keywords or functions as your variable names.
C. Enclosing Namespace
Alright, this is my favorite one. While working with the nested-functions (i.e., when you have function inside a function/s 👇), you’re implicitly creating an enclosing namespace inside the outer() function.
Allowing inner() function to access variables defined in the outer() function, while still maintaining its own local scope.
# outer is the enclosing scope or non-local scope
def outer():
def inner():
print("inner function") # Output: inner function
# inner is the local scope
inner()
print("outer function") # Output: outer function
outer()
# main is the global scope
print("main program") # Output: main program
This enables the inner() to work with data from outer() without affecting the other parts of the program.
— Again, can you modify the ‘enclosing` variable from the `local` scope?
Similar to global keyword Python offers nonlocal keyword to modify enclosing variable.
# outer is the enclosing scope or non-local scope
def outer():
a = 1 # `a` is in the enclosing (non-local) scope
def inner():
nonlocal a
a += 1 # modifies the non-local a using `nonlocal` keyword
print('inner function', a) # Output: inner function 2
# inner is the local scope
inner()
print("outer function", a) # Output: outer function 2
outer()
# main is the global scope
print("main program") # Output: main program
Again it isn’t recommended to do so, as it is not a good programming practice, but you have this flexibility.
Let’s see the LEGB Rule in Action!
In Python, different isolated namespaces, have their respective associated scopes. These are:
1. Local scope: Scope of the name/s created inside the functions. These names exist only while the function is running and can’t be accessed outside the function.
# outer is the enclosing scope or non-local scope
def outer():
a = 3
def inner():
a = 5 # accesses the local `a`
print("inner function", a) # Output: inner function 5
# inner is the local scope
inner()
print("outer function") # Output: outer function
a = 1
outer()
# main is the global scope
print("main program") # Output: main program
# We got a value form local scope hence a = 5
2. Enclosing or `nonlocal` scope: While working with nested functions, the name/s in the outer function belongs to the enclosing or nonlocal scope. It may contain non-local, as well as non-global names.
# outer is the enclosing scope or non-local scope
def outer():
a = 3 # `a` is in the enclosing (non-local) scope
def inner():
# accesses the non-local `a`
print("inner function", a) # Output: inner function: 3
# inner is the local scope
inner()
print("outer function") # Output: outer function
a = 1
outer()
# main is the global scope
print("main program") # Output: main program
# We got a value form enclosing scope, since there was
# no local scope for a, hence a = 3
3. Global scope: Name/s created inside the __main__ module, falls under this scope. You can access them anywhere in the current module.
# outer is the enclosing scope or non-local scope
def outer():
def inner():
# accesses global `a`
print("inner function", a) # Output: inner function 1
# inner is the local scope
inner()
print("outer function") # Output: outer function
a = 1 # `a` is in the global scope
outer()
# main is the global scope
print("main program") # Output: main program
# We got a value from the global scope, since there was
# no local and enclosing scope for `a`, hence a = 1
4. Built-in scope: The scope of all the pre-defined keywords and functions that Python offers. These are available as soon as the interpreter starts and never gets deleted.
# outer is the enclosing scope or non-local scope
def outer():
def inner():
# uses built-in max function on global variable `a`
print("inner function", max(a)) # Output: inner function 2
# inner is the local scope
inner()
print("outer function") # Output: outer function
a = [1,2] # `a` is in the global scope
outer()
# main is the global scope
print("main program") # Output: main program
5. NameError: Occurs when you try to use a name or function name that has not been defined or is not accessible in the current scope, as per the LEGB Rule.
# outer is the enclosing scope or non-local scope
def outer():
def inner():
print("inner function", a) # attempts to access a, but it doesn't exist in any scope
# inner is the local scope
inner()
print("outer function")
outer()
# main is the global scope
print("main program")
# This code will result in a NameError: name 'a' is not defined
# because `a` is not defined in local, enclosing, global, or built-in scopes.
Note: Depending upon where they are defined each scope corresponds to different levels of access and lifespan for namespace/s, within the code.
Conclusion
To sum up, namespace is a dictionary from where you can find a variable name and its value. This namespace is stored in the scope of python.
Scopes are of 4 types: local, enclosing, global and built-in. Remember, from current scope you will go upward in the hierarchy of LEGB Rule.
Also, if you want to modify a variable in global scope from local scope, yopu can use the global keyword. Similarly for enclosing scope, you can use nonlocal keyword.
Additionally, you must know about these 10 Everyday Tips and Tricks to Write Better Python Codes, here.👈🏻
With that said, this going be the wrap up. Wanted to give you a quick comprehensive guide to namespaces and scopes, as python developer you should know.
If you’ve learned to code more effectively today, Clap 50 👏 times as a token of appreciation and, do not forget to follow for future updates.
And before you go… If you have any questions/suggestions/thoughts, do drop me a line below. 🖋️
That’s it from me. Will talk soon! 🙋🏻♀️
— Nikita Prasad
Stop Struggling with Namespaces and Scopes in Python was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.
This content originally appeared on Level Up Coding - Medium and was authored by Nikita Prasad
Nikita Prasad | Sciencx (2024-08-14T11:20:28+00:00) Stop Struggling with Namespaces and Scopes in Python. Retrieved from https://www.scien.cx/2024/08/14/stop-struggling-with-namespaces-and-scopes-in-python/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.