What is a Namespace and scope in Python?

In a classroom full of students, there is a high possibility of at least two students having the same name. How do we refer to those students? We'll use the last name or surname that identifies each person uniquely. In the Python classroom of objects, there is a need for individual identity. With a huge number of objects, Python uses the concept of Namespaces to manage the names. In this tutorial, we'll learn what the word "Namespace" means and many more concepts, along with examples.

An object in Python can be anything from a variable to a method. Python stores all the names of the objects in the form of a dictionary with names as keys and objects as values:

Dict = {name1: obj1, name2: obj2…}

Definition of a Namespace:

A namespace in Python is the collection of objects defined along with the names they're defined within the form of a dictionary. Every name: object (key: value) pair in the namespace explains about one corresponding object.

Python has a lot of built-in libraries and so a lot of built-in objects. To check all the built-in objects in Python:

  1. Open cmd
  2. Type python3 or py
  3. Use the command:

Output:

['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
  • builtins is a module in Python that has all the built-in identifiers in Python. We used the dir() method to get the module's list of attributes and methods.
  • This namespace is called the Built-in namespace, and the interpreter creates it when we launch Python and holds it till we terminate the session.

Python identifies the objects using the namespaces. How does it identify two objects with the same name?

First, there are 3 types of Namespaces in Python:

  1. We already learned about the Built-in Namespace
  2. Global Namespace
  3. Local Namespace

As the names suggest:

  1. The built-in namespace is the collection of names of built-in objects in Python
  2. The global namespace is the collection of names of user-defined objects defined in the main body of the program. Python also creates a global namespace for the modules and libraries we import into our program.
  3. Python creates a namespace to a function when it finds a call to the function and reaches the function definition. The local namespace collects names of user-defined objects defined inside function bodies. These namespaces are maintained only until the function is executed.

Look at the following example:

  • There is a function-enclosing_function, and in its body, there is another function defined-enclosed_function.
  • First, the name of the enclosing_function is stored in the global namespace, and the main program calls the enclosing_function and creates a namespace for it.
  • The name of the enclosed_function is stored in the namespace of the enclosing_function.
  • The enclosing_function calls the enclosed_function and creates a namespace for it.
  • Hence, there are two local namespaces in the above example. Every namespace exists only until the corresponding function terminates.
Global namespaceEnclosing namespaceEnclosed namespace
Enclosing_functionEnclosed_function

Note that in a single namespace, there can't be two objects of the name type with the same name. There can be objects with the same name and types in different namespaces. An object can hence exist in more than one namespace simultaneously.

Here comes another question:

How does Python find out which object we refer to when objects with the same name exist in multiple namespaces?

The answer lies in a concept called "The Scope".

The scope of an object is the part of the program in which it has meaning. Based on where the object is defined and the parts where it is referenced, the interpreter determines the scope of the object.

When we refer to an object in the program to find it, Python follows a rule called the LEGB Rule

L: Local namespace

E: Enclosing namespace

G: Global namespace

B: Built-in namespace

Suppose we referred to a variable a:

  • The interpreter first searches inside the local function body of the referral.
  • If a isn't found and there is another function enclosing the local function, it searches for a in the body of the enclosing function.
  • Then, it looks in the global namespace.
  • Finally, if neither of the above searches goes successful, it searches in the built-in namespace of Python.
  • If the interpreter cannot find a in any of the above searches, it means a isn't defined. It raises the NameError Exception.

Here are a few examples:

Example1:

Output:

object

Understanding:

In the above program, we are referring to a inside the enclosed_function().

The namespaces:

Global namespaceEnclosing namespaceEnclosed namespace
Enclosing_function, aEnclosed_function

The search will go like this:

  1. L: Enclosed namespace: Not found
  2. E: Enclosing namespace: Not found
  3. G: Global namespace: found

Example 2:

Output:

Local object

Understanding:

We referred to a inside the enclosed_function()

The namespaces:

Global namespaceEnclosing namespaceEnclosed namespace
Enclosing_function, aEnclosed_functiona

The search goes like this:

1. L: Enclosed namespace: Found

The interpreter prints the value of a in the enclosed namespace and doesn't search any further.

Example 3:

Output:

enclosing

Understanding:

We referred to a inside the enclosed_function()

The namespaces:

Global namespaceEnclosing namespaceEnclosed namespace
Enclosing_function, aEnclosed_function, a

The search goes like this:

L: Enclosed namespace: Not found

E: Enclosing namespace: Found

Example 4:

Output:

NameError: name 'a' is not defined

Understanding:

We referred to a inside the enclosing_function()

The namespaces:

Global namespaceEnclosing namespaceEnclosed namespace
Enclosing_functionEnclosed_functiona

The search goes like this:

L: Enclosing namespace: Not found

E: No enclosing function

G: Not found

B: Not found

Hence, NameError

  • Even if a is defined in the Enclosed namespace, as we discussed earlier, the namespace of a function is maintained only until the function is terminated. The scope of the enclosed function is hence restricted to its body.

Now that we learned about types of namespaces, here is a point to grab:

  • Global and local namespaces are implemented in the form of dictionaries, as we discussed earlier.
  • The built-in namespace is not implemented as a dictionary. It is implemented as a module.

Accessing the namespaces:

We already accessed the built-in namespace using the dir(__builtins__) earlier in the tutorial. We'll now learn how to access the global and local namespaces:

The Global namespace:

We can access the global namespace using a built-in function called globals()

In interactive mode (cmd):

Output:

{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': , '__spec__': None, '__annotations__': {}, '__builtins__': , 'a': 'Global'}

Understanding:

'a' : 'Global'

Here, a is the name, and 'Global' is the object.

We can also use the function in an IDE-> print(globals())

Here is another example:

Output:

{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': , '__spec__': None, '__annotations__': {}, '__builtins__': , 'a': 'Global', 'f': }

Understanding:

We created a function f() in which we defined a local variable b. When we called globals():

  1. a is in the namespace, and b isn't as it is a local variable.
  2. The function's name is in the namespace and note that it is only the name, and the function will have its local namespace with b in it.

Depending on your application, the dictionary can have different names. As it is a dictionary, we can perform some operations like:

  1. Comparison (is)
  2. Add entries
  3. Modify existing entries

Local namespace:

Like global() for the global namespace, there is a function locals() to access the local namespace. It should be called inside a function to get the function's namespace. If it is used outside the function, it will act like globals().

Here is an example:

Output:

{'b': 1, 'c': 2}
3

Some important points to grab:

  1. globals() returns the original global namespace dictionary. Every change and variable we perform shows up in the original global namespace. You can observe this in the examples above.
  2. locals(), on the other hand, returns the copy of the original local namespace. Hence, even if we make any changes, like adding entries, we will make these changes only in the copy we used and not on the original local namespace.

Here is an example:

Output:

{'a': 3, 'b': 4}
{'a': 5, 'b': 4}
{'a': 3, 'b': 4, 'local': {...}}

Understanding:

In the above example, we created a function add(). Python creates a local namespace for the function add(). We stored the locals() dictionary in the local variable. We modified the local variable. Observe that the change is not reflected in the original local namespace, and the copy we created locally is added to the local namespace. Hence, we can't make changes to the original local namespace like we can to the global namespace.

Modifications:

We learned that two objects with the same name could exist in different scopes. Now, we'll see which arguments can be modified in which scopes and which can't be.

Here are two important points:

  1. An immutable object can't be modified outside its scope.
  2. A mutable object can be modified but can't be reassigned.

Let us understand the first point with an example:

Strings are immutable. We defined a string a in the global scope, and inside the function f(), we tried to modify the value of a. Hence, the value of a in the global scope is printed.

Mutable objects:

Lists are mutable. Hence, it is modified in the local scope.

If we try to reassign a mutable object:

When we reassign the value of a in the local scope, a new local object will be created as two objects of the same name can be stored in two different namespaces. Hence, when we refer to a in the global namespace, a in the global namespace is printed.

Python never leaves us without a way. If we need to modify an immutable object in another scope:

  1. Use the global declaration
  2. Use the globals() function
  3. Use the nonlocal declaration

1. global declaration:

Suppose we want to modify a global object inside a function (local scope). To stop Python from creating a new local object, we can refer to the global object using the global reference:

By the statement global a, we're telling the interpreter that any references to a inside the function are actually to the global object a.

What if we refer to a global object that isn't even in the global namespace?

Yes, Python creates the object and places it in the global namespace.

2. globals() function:

Using this function, we can access the global namespace and update the object:

3. nonlocal declaration

If we want to modify an object of the enclosing function inside the enclosed function, we can use the nonlocal declaration. Global declaration or function doesn't work here as the object we want to modify isn't in the global namespace but the namespace of the enclosing function.

Here is an example:

  • However, modifying an object in another scope isn't suggested. We can rather use a function with a return value.

End checklist:

  1. What is a namespace?
  2. Types of namespaces.
  3. Accessing the namespace.
  4. Python's way of searching for an object.
  5. Modifying objects.
  6. Modifying objects in other scopes.





Latest Courses