Python Namespaces & Variable Scope
How Python figures out the scope of it's variables?
Namespaces are one honking great idea—let’s do more of those!
— The Zen of Python, by Tim Peters
The Namespace is one of the most beautiful concepts in Python. Bound vs Unbound variable sheds a little light on how variables are accessed and their scope. But let's dive deeper and see how it happens exactly
Think of the namespaces of a dictionary in a Python program. Namespaces are created at different moments and have different lifetimes. Every time you create an object(variables, functions), an associated namespace gets updated so that the next time you refer to the same variable, you can get its value.
Open Python shell and type the following
What do you see?
There are different kinds of namespaces. Let's look at each of them in detail.
Built-in Namespace
In Python, we don't have to import certain functions or objects, they seem to be available to us in handy. But how?
The namespace containing the built-in names, such as sum, max, etc., is created when the Python interpreter starts up (before even globals) and is never deleted. The built-in names actually also live in a python module; this is called builtins
All the built-in objects are loaded into the program via __builtins__
namespace
Global Namespace
The global namespace for a module is created when the module definition is read in; normally, module namespaces also last until the interpreter quits.
The statements executed by the top-level invocation of the interpreter, either read from a script file or interactively, are considered part of a module called main, so they have their own global namespace
Output
Think of globals as a universal dictionary for the module. Every time you create an object(including function) or import a module at the module level(ie., outside all functions), the global namespace gets updated. On deleting the objects, the same gets removed from the namespace
Local Namespace
The local namespace for a function is created when the function is created and deleted when the function returns or raises an exception that is not handled within the function. (Actually, forgetting would be a better way to describe what actually happens.)
Each recursive invocation of a function will have its own local namespace.
In fact, all operations that introduce new names use the local scope: in particular, import statements and function definitions bind the module or function name in the local scope.
Let's take a look at the following snippet of code. There are three things to notice
There are two variables with the same name
name
They are printed at two different places - Inside & outside the function
The function is called to see what it prints
Output
As expected, the global variable's value was not affected by the local variable despite it being the same variable name. That is because
The local namespace is different from the global namespace
The Python interpreter looks for variables in the nearest local namespace before escalating the search to the global namespace
Locals Method
Similar to globals()
you can use locals()
to get all the local objects pertaining to that scope
At global scope `globals()` and `locals()` value is the same. Voila
Referring Global Variables
Now, what if there was a case where you had to tell the function scope to use the variable defined in the global
scope? Worry not! We have a global
variable.
In the above snippet, the global keyword is used to refer to the global name
, thereby updating it to a new value.
Output
Also, notice that the `locals()` dict remains untouched since there are no variables defined in the local scope.
Enclosing-Namespace
Python is known for its nested functions. Oh! those decorators! Yet another fascinating topic. More on that later. Let's take the following function as an example
We have a variable new_1
defined in 3 different scopes Inner function, outer function, and global, and is printed at each level
Output
The magic begins when you start to comment out new_1
variable from the innermost scope to the outer.
Comment new_1 = "inner_1"
and the output becomes
Comment new_1 = "outer_1"
and the output becomes
We went through all this trouble to understand that the variable resolves itself to the nearest enclosing scope. In our case, `outer_function
`.
nonlocal keyword
Similar to global
keyword nonlocal
is used to refer to variables at the enclosing namespace
Modifying the value of nonlocal variables changes the variable in the enclosing namespace.
Output
Global vs. Non-local
Using global
keyword instead of nonlocal
will directly refer to the global variable discarding all other enclosing scopes
Output
Scope of a Variable
We have been referring scope
throughout this post. It's high time that we provide a formal definition of it
A scope is a textual region of a Python program where a namespace is directly accessible. “Directly accessible” here means that an unqualified reference to a name attempts to find the name in the namespace.
When a variable is referred in the code, the Python interpreter searches for it in the following order
Local scope
Enclosing scope
Global scope
Built-in scope
Exercise For You
Import a module, let's say `import math` in different scopes global, enclosing, and local. Find out what `globals()` and `locals()` method outputs in each case
Last updated