Python Simplified

PythonSimplifiedcomLogo

Understanding namespaces and scope in Python

Namespaces and Scope in Python

Introduction

In Python, everything is an object. A Python module consists of hundreds of objects (integer objects, strings, functions, classes, etc.) and all these objects are maintained as a dictionary in different namespaces. Access to these namespaces is determined by the scope you are currently in. 

Namespaces and scope are important concepts that every Python developer should be aware of. In this article, you will learn namespaces and scope in Python with examples, the LEGB rule, globals() & locals() built-in functions, and finally global and nonlocal keywords. 

Namespaces

As defined in the official documentation, “A namespace is the mapping between names and objects”. The name can be a variable name, function, class, method, etc., or any Python object.

Let’s try to understand with a simple example. In the below example, we have three objects — an integer object, a string object, and a function. 

				
					>>> x = 100

>>> Author = "Chetan Ambi"

>>> def myfunc():
...     print("Python Simplified !!")
				
			

The above objects are stored as {‘x’: 100, ‘author’: ‘Chetan Ambi’, ‘myfunc’: } in the namespace along with other objects.

When the Python program is in execution, one or more namespaces exist. The access to names and objects in any of these namespaces is determined by the scope you are currently in. So what are scopes? It will be covered in the next section. 

Types of namespaces in Python

Types of namespaces

  • Local/function namespace — includes all the names declared within a function/class including enclosing functions.
  • Builtin namespace — includes all the built-in functions, objects related to exceptions, etc. It is created and accessible as long as the Python interpreter is up and running. You can refer to the list of all built-in names using dir(__builtins__).
  • Global namespace — includes all the names of a Python module (a single .py file). It means all the variable names, functions, classes and objects, etc. defined in a Python module (.py file) are included in the global namespace.

Scope

As defined in the official documentation, “A scope is a textual region of a Python program where a namespace is directly accessible.”. It means that, as mentioned earlier, the access to names is determined by the scope you are currently in. 

Types of scopes

  • Local scope — it includes the scope inside function/class. All the names defined in the function/class are only accessible inside the function/class and are not accessible outside. 
  • Non-local/Enclosing scope — the names that are non-local are neither present in the local scope or in the global scope. Their scope is midway between local and global scope. An example for this is a function defined inside another function is the enclosing function itself.
  • Global scope — it is also called module scope (single .py file). It covers the scope outside the function/class but within the same module.
  • Built-in scope — it is built into Python and is accessible anywhere inside the module. 
Types of scope in Python

LEGB Rule

LEGB stands for Local Enclosing Global Built-in. The LEGB is a rule used by the Python interpreter when looking for a variable. Python interpreter first looks into namespace corresponding to the local scope, then enclosing scope, followed by the global scope, and finally built-in scope. Only if it doesn’t find a reference in any of the namespaces then it will throw an error.

LEGB rule

Enough of theory. Let’s try to understand with some examples. Once you go through the below examples, I am sure you will be clear about namespaces and scope concepts in Python.

Examples

Before we actually start looking at code examples, let’ see the below diagram that helps you understand as you go through the examples.

Namescopes and scope example

(1) In the below example, there is only one statement. Since we are in global/module scope, the Python interpreter first checks if there is any function with the name print in the global/module scope. Since print is not part of the global scope, it will then look into the next scope i.e. built-in scope. As print is part of the built-in scope, the below statement executes without any errors.

				
					# module1.py
>>> print("Python Simplified")
Python Simplified
				
			

Let’s assume that you created a function with the name print in the module scope as below. Now, when you call print(), it first looks into the global scope i.e. module1.py scope. Since print function is defined in the global/module scope that will get executed by Python. Since the reference to print is found the global scope, interpreter will not check in the built-in scope.

				
					# module1.py
>>> def print():
...     return "Python Simplified"

>>> print()
Python Simplified
				
			

(2) The below example has a nested function. The x = 100 is in the global scope. But you can still access its value in the local scope i.e. both inside outer and inner functions. How? The answer is as per the LEGB rule. 

When running line#5, Python doesn’t find the reference in the local (outer) scope, so it looks for x in the next scope i.e global scope and it finds reference to x in the global scope i.e. x= 100. 

A similar logic applies to line#8. Python doesn’t find reference to x in the local scope (inner function) and also in the enclosing scope (outer function) but finds reference in the global scope. Hence, the value of x i.e. 100 from the global scope is used. 

				
					# module2.py
>>> x = 100

>>> def outer():
...     print("outer", x)
...     
...     def inner():
...         print("inner", x)
...        
...     inner()

>>> outer()
print("global", x)
				
			

Output:

				
					outer 100
inner 100
global 100
				
			

(3). Let’s look at the same example as shown above but with little modification. We are assigning x = 200 in the outer function. 

Even though you modified the value of x in the outer function, the modified value x = 200 will not be reflected global scope. Because x = 200 scope is within the outer function. And inside inner function, x is not defined. Hence, Python first checks in the enclosing scope i.e. outer function scope, and finds the reference to x in the outer function. 

				
					>>> x = 100

>>> def outer():
...     x = 200
...     print("outer", x)
...     
...     def inner():
...         print("inner", x)
...        
...    inner()

>>> outer()
>>> print("global", x)
				
			

Output:

				
					outer 200
inner 200
global 100
				
			

Global and nonlocal keywords

Python also provides two keywords global and nonlocal for special cases. Let’s see a couple of examples to understand. 

Global keyword — The global keyword allows you to modify the global variable inside a local scope. 

As you can see from the below example, with the help of the global keyword, the change made to the variable x inside the local scope is effective in the global scope as well. As seen in previous examples, without a global keyword, you won’t be able to change the values in the global namespace.

				
					>>> x = 100
>>> print("global (before):", x)

>>> def outer():    
...     def inner():
...         global x
...         x = 200
...         print("inner:", x)
...     inner()

>>> outer()
>>> print("global (after): ", x)
				
			

Output:

				
					global (before): 100
inner: 200
global (after):  200
				
			

Nonlocal keyword — The nonlocal keyword is used when nested functions are involved and is used to change the value of a variable of the enclosing scope. The scope should not be local or global scope. 

As you can see from the below example, x is made nonlocal in the inner function. It means that it references the first occurrence of x in the enclosing scope which is an outer function in this case. So any changes made to x (i.e. x = 300) are visible only in the outer function but not in the global scope. In the global scope, the value of x is still 100.

				
					>>> x = 100

>>> def outer():    
...     x = 200
...    
...     def inner():
...         nonlocal x
...         x = 300
...    
...     print("outer (before):", x)
...     inner()
...     print("outer (after):", x)

>>> outer()
>>> print("global:", x)
				
			

Output:

				
					outer (before): 200
outer (after): 300
global: 100
				
			

Globals() and Locals()

As mentioned earlier, Python stores names and objects as dictionaries. You can see what is the content of the current global namespace and local namespace using the two built-in functions — globals() and locals().

Globals() — The built-in function globals() returns the current global namespace as a dictionary. By default, it contains some predefined objects as you can see below. After adding x = 100 in the global scope, you will see that a new object has been added to the globals namespace.

				
					>>> print(globals())
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000026F36BF6D00>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:\\Users\\swath\\PycharmProjects\\pythonProject\\test.py', '__cached__': None}

				
			
				
					>>> x = 100
>>> print(globals())
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001A8C3126D00>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:\\Users\\swath\\PycharmProjects\\pythonProject\\test.py', '__cached__': None, 'x': 100}
				
			

Locals() — The built-in function locals() returns the current local namespace as a dictionary. In the below examples, before the assignment x = 100, locals() returned as an empty dictionary as expected and after the assignment, it contains one element in the dictionary. 

				
					>>> def my_func():
...     print("local -> before assignment: ", locals())
...     x = 100
...     print("local -> after assignment: ", locals())

>>> my_func()
				
			
				
					local -> before assignment:  {}
local -> after assignment:  {'x': 100}
				
			

Summary

  • A namespace is the mapping between names and objects. The name can be a variable name, function, class, method, etc., or any Python object.
  • There are different types of namespaces – local/enclosing namespace, global namespace, and built-in namespace.
  • A scope is a textual region of a Python program where a namespace is directly accessible.

  • There are different scope – local scope, enclosing scope, global scope, and built-in scope.

  • The LEGB (Local Enclosing Global Built-in) is rule or the order used by Python interpreter when looking for the variable resolution. 
  • The global keyword allows you to modify the global variable inside local scope whereas nonlocal keyword allows you the modify the value of the variable of the  enclosing scope.
  • The global() function returns the names of the current global scope whereas locals() returns the values of the current local scope.

References

Share on facebook
Share on twitter
Share on linkedin
Share on whatsapp
Share on email
Chetan Ambi

Chetan Ambi

A Software Engineer & Team Lead with over 10+ years of IT experience, a Technical Blogger with a passion for cutting edge technology. Currently working in the field of Python, Machine Learning & Data Science. Chetan Ambi holds a Bachelor of Engineering Degree in Computer Science.
Scroll to Top