Session 23 : Namespaces in Python
Namespaces
Namespaces in Python are essential concepts that refer to a system that holds a collection of names as keys and their corresponding objects as values. They are used to organize variables and avoid conflicts by creating separate, distinct regions for different names.
A namespace is a space that holds names(identifiers).
programically namespace are dictionary of identifiers and their objects
In [ ]: a=2
b=3
{a:2}
{b:3}
Types of Namespaces in Python
1. Built-in Namespace
Contains built-in functions and exceptions that come with Python, such as print() , len() , and Exception .
This namespace is loaded when the Python interpreter starts and is available globally in any part of the program.
2. Global Namespace
Contains names defined at the level of the main program, or names defined in a module imported into the main program.
Variables, functions, and classes declared outside of functions or classes are part of the global namespace.
Each module has its own global namespace.
3. Local Namespace
Created for each function or method call.
Holds names defined inside a function or method.
The scope of these names is limited to the function, meaning they are only accessible within that function.
4. Enclosing Namespace (Nonlocal)
Created when there are nested functions. If a function is defined inside another function, it has access to the outer function’s namespace.
Variables in the enclosing function are part of the enclosing (nonlocal) namespace for the inner function.
Uses the nonlocal keyword to modify variables from the outer function scope.
Namespace Hierarchy and the LEGB Rule
When Python encounters a name, it follows the LEGB Rule to resolve it:
1. Local: Checks the innermost scope (local to the function or method).
2. Enclosing: Checks any enclosing function’s namespace, if present.
3. Global: Checks the module-level namespace.
4. Built-in: Checks the built-in namespace for any built-in functions or variables.
Scope and LEGB Rule
A scope is a textual region of a Python program where a namespace is directly accessible. The interpreter searches for a name from the inside out, looking in the local, enclosing, global, and finally the built-in scope. If the interpreter doesn’t find the name in any of these locations, then
Python raises a NameError exception.
Global Scope and Local Scope
In Python, the global scope is the top-level scope in which variables are defined outside of any functions or classes. Variables in the global scope are accessible from any part of the code, including within functions and classes, unless shadowed by a local variable of the same name.
In Python, local and global scopes define where variables are accessible within a program.
Local Scope
A local scope is the region within a function or block of code where variables created within that block are accessible. Variables created in a local scope are called local variables, and they only exist during the execution of that specific function.
Lifetime: Local variables are only available while the function is running and are destroyed once the function completes.
Isolation: Each function has its own local scope, so variables within one function do not affect variables within another function, even if they share the same name.
Global Scope
A global scope is the region where variables are accessible throughout the entire program, including inside and outside functions. Variables created outside of any function or block are considered global variables, and they are accessible from any function in the program unless
shadowed by a local variable of the same name.
Lifetime: Global variables exist for the duration of the program.
Access in Functions: To modify a global variable inside a function, you must declare it with the global keyword.
Key Differences
1. Accessibility:
Local variables: Only accessible within their defining function.
Global variables: Accessible throughout the program.
2. Lifetime:
Local variables: Exist only during the function's execution.
Global variables: Exist throughout the program's lifetime.
3. Declaration:
Local variables: Declared inside functions or blocks.
Global variables: Declared outside any function or block.
In [1]: # global VS local
a=2 #global variable
def temp():
b=3 #local variable
print(b)
temp()
print(a)
3
2
In [3]: # local and global -> same name
a=2 #global variable
def temp():
a=3 #local variable
print(a)
temp()
print(a)
3
2
In [4]: # local and global -> local does not have but global has
a=2 #global variable
def temp():
print(a)
temp()
print(a)
2
2
In [5]: # local and global -> editing global
# Local can only access the global but cannot modify it, can see it but cannot edit it
a=2 #global variable
def temp():
#local var
a+=1
print(a)
temp()
print(a)
---------------------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
Cell In[5], line 8
5 a+=1
6 print(a)
----> 8 temp()
9 print(a)
Cell In[5], line 5, in temp()
3 def temp():
4 #local var
----> 5 a+=1
6 print(a)
UnboundLocalError: cannot access local variable 'a' where it is not associated with a value
In [6]: # Global Keyword
a=2 #global variable
def temp():
global a
a+=1
print(a)
temp()
print(a)
3
3
In Python, the global keyword is used to declare that a variable inside a function refers to a globally defined variable outside the function's scope. By default, when you assign a value to a variable within a function, Python treats it as a local variable. If you want to modify a global
variable inside a function, you need to declare it as global .
How global Works
When you use global variable_name inside a function:
1. Python searches for variable_name in the global namespace instead of creating a new local variable.
2. Changes to variable_name within the function will affect the global variable outside the function.
When to Use global
Use global when you need to modify a global variable inside a function.
Avoid using global excessively, as it can make code harder to read and maintain by tightly coupling functions with global state.
Limitations
global cannot be used to declare variables outside of all functions in the script or module scope.
It only affects variable references within the function where it's declared, not across other modules.
In [7]: # local and global -> global created inside local
def temp():
global a
a=1
print(a)
temp()
print(a)
1
1
In [13]: # local and global -> function parameter is local
def temp(z):
print(z)
a=5
temp(5)
print(a)
print(z)
5
5
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[13], line 7
5 temp(5)
6 print(a)
----> 7 print(z)
NameError: name 'z' is not defined
Built-in Scope
The built-in scope in Python is the outermost scope where Python's built-in functions, constants, and exceptions reside. This includes functions like print() , len() , sum() , and constants such as True , False , and None . The built-in scope is part of Python's standard library,
and the objects defined in it are available globally in any Python script or module.
How the Built-in Scope Works
1. Accessibility: Objects in the built-in scope are accessible throughout a Python program without needing to import or define them in each file. For example, you can use print() or len() anywhere in your code without any specific import statement.
2. Immutability: Since the built-in scope is defined when Python starts up, objects in this scope are essentially immutable for practical purposes, meaning they should not (and usually cannot) be redefined. However, technically, if you assign True = False , Python will allow it, but
this will cause severe bugs and break the program's logic.
3. Namespace Hierarchy: Python resolves variable names by looking up the namespace hierarchy: local → enclosing → global → built-in. If a name isn’t found in the local, enclosing, or global scope, Python finally checks the built-in scope.
In [14]: # Builtin scope
print("hello")
hello
In [15]: # Accessing All Built-ins
import builtins
print(dir(builtins))
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BaseExceptionGroup', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'Conn
ectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EncodingWarning', 'EnvironmentError', 'Exception', 'ExceptionGroup', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'Fu
tureWarning', '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', 'ResourceWarnin
g', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'Unic
odeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError', '__IPYTHON__', '__build_class__', '__debug__', '__doc__', '__import__', '__loader_
_', '__name__', '__package__', '__spec__', 'abs', 'aiter', 'all', 'anext', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'd
ict', 'dir', 'display', 'divmod', 'enumerate', 'eval', 'exec', 'execfile', 'filter', 'float', 'format', 'frozenset', 'get_ipython', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclas
s', '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', 'sorte
d', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
Modifying Built-in Scope (Not Recommended)
In [1]: # Don't do this!
len = 5
print(len([1, 2, 3])) # This will raise a TypeError because `len` is now an integer, not a function
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[1], line 5
1 ### Modifying Built-in Scope (Not Recommended)
2
3 # Don't do this!
4 len = 5
----> 5 print(len([1, 2, 3]))
TypeError: 'int' object is not callable
In [22]: # Avoid naming your variables after built-in functions or constants to prevent accidental overrides.
L=[1,2,3]
max(L)
def max():
print("hello")
max(L)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[22], line 4
1 #rename built-ins
3 L=[1,2,3]
----> 4 max(L)
6 def max():
7 print("hello")
TypeError: max() takes 0 positional arguments but 1 was given
Enclosing Scope
The enclosing scope in Python refers to the scope of any outer, or "enclosing," function within which a nested (inner) function is defined. This is sometimes also called the "nonlocal" scope. When an inner function tries to access a variable that isn’t defined locally, Python will check the
enclosing function’s scope to resolve the variable before moving on to the global or built-in scopes.
How the Enclosing Scope Works
1. Nested Functions: Enclosing scopes are created by nested functions. If you define a function within another function, the outer function's scope becomes the enclosing scope for the inner function.
2. Name Resolution: Python resolves variable names by following this order: local → enclosing → global → built-in.
3. Nonlocal Keyword: If you want to modify a variable from the enclosing scope inside the inner function, you must use the nonlocal keyword. Otherwise, assigning to a variable in the inner function will create a new local variable, not modifying the one in the enclosing scope.
In [30]: # enclosing scope
def outer():
def inner():
print(a)
inner()
print("outer function")
outer()
print("main program")
1
outer function
main program
In [29]: # nonlocal keyword
def outer():
def inner():
print(a)
inner()
print("outer")
#b=1
outer()
print("main program")
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[29], line 12
9 print("outer")
11 #b=1
---> 12 outer()
13 print("main program")
Cell In[29], line 8, in outer()
5 def inner():
7 print(b)
----> 8 inner()
9 print("outer")
Cell In[29], line 7, in outer.<locals>.inner()
5 def inner():
----> 7 print(b)
NameError: name 'b' is not defined
Modifying Enclosing Scope Variables with nonlocal
Here, nonlocal allows inner_function to modify the message variable from outer_function . Without nonlocal , assigning a new value to message inside inner_function would create a new local variable named message without affecting the enclosing scope.
In [32]: # nonlocal keyword
def outer():
a=1
def inner():
nonlocal a
a+=1
print("inner",a)
inner()
print("outer",a)
#b=1
outer()
print("main program")
inner 2
outer 2
main program