Python Namespace and Scope



What is a Name in Python?

A Name (or identifier) is a name given to objects. Everything in Python is an object, and a Name is a way to access all those objects.

For example, the statement x = 7 is an assignment where `` is an object stored in memory and the x is the name that references that object. In Python, you can get the address (in RAM) of objects using the built-in function id().

x = 7
print("id(7) = ", id(7))
print("id(x) = ", id(x))

Output:

id(7) =  94165220059840
id(x) =  94165220059840

As you can see above, both the name x and the object 7 have the same memory address because they reference the same object.

Let us see the following example:

x = 6
print("id(x) = ", id(x))

x = x + 1
print("id(x) = ", id(x))

print("id(7) = ", id(7))

y = 6
print("id(y) = ", id(y))
print("id(6) = ", id(6))

z = 7
print("id(z) = ", id(z))

print("id(7) = ", id(7))

After executing the above code, the output will be as follows:

id(x) =  94165220059808
id(x) =  94165220059840
id(7) =  94165220059840
id(y) =  94165220059808
id(6) =  94165220059808
id(z) =  94165220059840
id(7) =  94165220059840

To explain the above output, let us see the following illustration:

Memory diagram of variables in Python

As we can see above, the object 6 is created, and the symbolic name x is associated with it. After running the statement x = x + 1, a new object 7 is created, and now x is associated with it.

After executing the statement y = 6, the new symbolic name y gets associated with the previous object 6.

Finally, after running the statement z = 7, the new symbolic name z gets associated with the object 7. So both names x and z are associated with the object 7.

Python does not have to create a new duplicate object to reference the same object. This dynamic nature of name binding makes Python efficient.

In Python, The same name could refer to any type of object.

>>> x = "Hello"
>>> x = 7
>>> x = ["apple", "orange", "kiwi"]

All the above statements are valid, and x will refer to three different types of objects.

In Python, everything is an object. So Functions are objects too, and a name can refer to them as well.


def greeting():
    print("Hello")

x = greeting()

x()

Output:

Hello

As we can see above, the name x can refer to a function, and we can also call the function using the name.


What is a Namespace in Python?

A namespace is a collection of defined symbolic names and information about the object that each name references.

You can imagine a namespace as a dictionary in which the keys are the object names, and the values are the objects themselves.

Namespaces are one honking great idea -- let's do more of those!

As Tim Peters said, namespaces are honking great, and Python uses them a lot.

In a Python program, there are four types of namespaces:

  • Built-int
  • Global
  • Enclosing
  • Local
Different namespace in Python

In Python, different namespaces can co-exist at a given time but are completely isolated. Therefore, the same name that may exist in different modules does not collide.


The Built-In Namespace

The built-in namespace contains the names of all Python built-in objects.

The built-in namespace objects are available at all times when Python is running.

To list the objects in the built-in namespace, you can run the following command:

>>> dir(__builtins__)
['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', 'ZeroDivisionError', '__IPYTHON__', '__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', 'display', 'divmod', 'dreload', 'enumerate', 'eval', 'exec', 'execfile', 'filter', 'float', 'format', 'frozenset', 'get_ipython', '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', 'range', 'repr', 'reversed', 'round', 'runfile', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']

As you can see above, there are some objects that you may know or used before, like the id(), int() etc ...

The built-in namespace is created when the Python interpreter is started and lives as long as it runs.


The Global Namespace

The global namespace holds any names defined at the level of the main program.

In Python, the global namespace is created when the main program body starts, and it stays in life until the interpreter terminates.

The Python interpreter also creates a global namespace for any module that you program loads using the import statement.


The Local and Enclosing Namespaces

The Python Interpreter creates a new namespace every time a function gets executed.

Let us see the following code:


def a():
    print("Start a()")
    

    def b():
        print("Start b()")
        print("End b()")
        return

    b()

    print("End a()")

    return

a()

After executing the above code, the output is as follows:

Start a()
Start b()
End b()
End a()

In the above code, the function b() is defined inside the body of the function a(). The following list describes what's happening in the code:

  • Lines 2 to 15 define a(), the enclosing function.
  • Lines 6 to 9 define b(), the enclosed function
  • On line 11, a() calls b().
  • On line 17, the main program calls a().

When the main program calls a(), the Python interpreter creates a new namespace for a() function. The same process happens when a() calls b(), b() gets its own namespace.

The namespace created for a() is the enclosing namespace, and the namespace created for b() is the local namespace.

Each of these namespaces stays in existence until its function finish.


Python Variable Scope

Even if there are different unique namespaces defined, you may not be able to access all of them from every part of the code. This is why the concept of scope comes in handy.

A scope is the portion of a program from where a namespace can be accessed directly without any prefix.

Python variable scope also defines the hierarchy in which we search for a variable.

There are four variables scope in Python:

  • Local - If you refer to the variable x inside a function, then the Python interpreter first searches for it in the inner scope that is the local scope to that function.
  • Enclosing - If the variable x is not in the local scope but exists in a function that resides inside another function, then the Python interpreter searches in the enclosing function's scope.
  • Global - If the Python interpreter did not found the variable x neither in local or enclosing scopes, then it looks in the global scope.
  • Built-in - If the variable x can not be found anywhere, then the Python interpreter tries the built-in scope.

Python Variable Scope Resolution Rules (LEGB)

In Python, variables are searched in the following order.

Local => Enclosed => Global => Built-in

This is called the LEGB rule for variable scope resolution.

Python Variable Scope Resolution - LEGB Rule

If the Python interpreter does not find the name in any of these locations, then a NameError exception is raised.


Scope and Namespace in Python - Example

Let us consider the following example:

def outer():
    y = "enlosed"
    
    def inner():
        z = "local"

x = "global"

Here, the variable x is in the global namespace. The variable y is in the local namespace of the outer(), and the variable c is in the nested local namespace for the inner() function.

When you are in the inner() function, the variable z is local, the variable y is enclosed, and the variable x is global. In the inner() function you can assign new values to z, but you can only read y and x from the inner() function.

If you are in the inner() function and try to assign a value to y or x, a new variable y or x are created in the local namespace of the inner() function, which is different from the enclosed y variable and the global x variable.

In case you declare x as a global variable, all the references and assignments go to the global x. The following example will explain more.

def outer():
    x = 2
    
    def inner():
        x = 3
        print("x =", x)

    inner()
    print("x =", x)

x = 1
outer()
print("x =", x)

The output of the above code is as follows:

x = 3
x = 2
x = 1

As we can see from the above result that three different variables x are defined in separate namespaces.

In the following example, we will use the global keyword to manipulate the global x variable.

def outer():
    global x
    x = 2
    
    def inner():
        global x
        x = 3
        print("x =", x)

    inner()
    print("x =", x)

x = 1
outer()
print("x =", x)

The output of the above code is as follows:

x = 3 
x = 3 
x = 3

As we can see in the above output here, all references and assignments concern the global x by using the keyword global.



ExpectoCode is optimized for learning. Tutorials and examples are constantly reviewed to avoid errors, but we cannot warrant full correctness of all content. While using this site, you agree to have read and accepted our terms of use, cookie and privacy policy.
Copyright 2020-2021 by ExpectoCode. All Rights Reserved.