globals() and locals() Functions Complexity¶
The globals() and locals() functions return dictionary representations of the current global and local namespaces, providing access to all defined variables in those scopes.
Complexity Reference¶
| Operation | Time | Space | Notes |
|---|---|---|---|
globals() |
O(1) | O(1) | Returns reference to existing global dict |
locals() |
O(1) or O(m) | O(1) or O(m) | Module/class scope is O(1); optimized function scopes materialize locals mapping |
| Accessing dict value | O(1) avg | O(1) | Dict key lookup; O(n) worst case with collisions |
Understanding Namespaces¶
Global Namespace¶
# Access global namespace - O(1)
x = 10
y = 20
global_vars = globals() # O(1) - returns reference to existing dict
print(global_vars['x']) # O(1) - access value
print(global_vars['y']) # O(1)
# globals() includes built-ins and module vars
print('print' in globals()) # Usually True (if imported)
Local Namespace¶
# Access local namespace - O(m)
def my_func():
a = 1
b = 2
local_vars = locals() # O(m) - creates dict
print(local_vars['a']) # O(1)
print(local_vars['b']) # O(1)
return local_vars
result = my_func() # O(m)
Common Patterns¶
Inspecting Variables¶
# List all global variables - O(n)
x = 10
y = 20
z = 30
all_vars = globals() # O(1)
# Filter variables (not functions/modules) - O(n)
my_vars = {k: v for k, v in all_vars.items()
if not k.startswith('_')}
print(my_vars) # {'x': 10, 'y': 20, 'z': 30}
Dynamic Variable Access¶
# Access variable by name - O(1)
var_name = 'x'
x = 42
# Using globals()
value = globals()[var_name] # O(1) - get value
# Using getattr for objects
class Config:
timeout = 30
retries = 3
config = Config()
attr_name = 'timeout'
value = getattr(config, attr_name) # O(1)
Getting Caller's Locals¶
import inspect
def get_caller_locals():
"""Get local variables of calling function"""
# Get calling frame
frame = inspect.currentframe().f_back # O(1)
# Access local variables - O(m)
caller_locals = frame.f_locals # O(m)
return caller_locals
def caller():
x = 10
y = 20
caller_vars = get_caller_locals() # O(m)
print(caller_vars) # {'x': 10, 'y': 20}
caller()
Function Scope¶
Module Level¶
# At module level, locals() == globals() - O(1)
X = 100
print(locals() is globals()) # True at module level
print(locals()['X']) # O(1)
Inside Functions¶
# Inside function, locals() is different - O(m)
global_x = 10
def func():
local_y = 20
# Different namespaces
print('local_y' in locals()) # True - O(1)
print('local_y' in globals()) # False - O(1)
print('global_x' in locals()) # False - O(1)
print('global_x' in globals()) # True - O(1)
func()
Modifying Namespaces¶
Setting Global Variables¶
# Modify global namespace - O(1)
globals()['new_var'] = 100 # O(1)
print(new_var) # 100 - variable created!
# Better approach: use globals() sparingly
# Prefer explicit assignment
new_var2 = 200
Setting Local Variables¶
# Modifying locals() has limited effect
def func():
locals()['x'] = 10 # O(1) - sets in dict
try:
print(x) # NameError! - x not actually in local scope
except NameError:
print("x not in local variables")
func()
# Don't use locals() to set variables in function
# Use explicit assignment instead
def func2():
x = 10 # Proper way
print(x) # Works
Practical Examples¶
Debug Function State¶
def debug_state(func_locals):
"""Print local variables with types"""
print("Local variables:")
for name, value in func_locals.items(): # O(m)
if not name.startswith('_'): # Skip special vars
print(f" {name}: {type(value).__name__} = {value}")
def example_func():
x = 42
s = "hello"
lst = [1, 2, 3]
debug_state(locals()) # O(m)
example_func()
Serialize Local Variables¶
import json
def save_state():
"""Save local variables to JSON"""
local_data = locals() # O(m)
# Filter serializable objects - O(m)
serializable = {}
for k, v in local_data.items():
if isinstance(v, (int, str, float, list, dict)):
serializable[k] = v
return json.dumps(serializable)
def task():
count = 10
name = "task"
items = [1, 2, 3]
state = save_state() # O(m)
return state
print(task())
Configuration Registry¶
# Use globals() as simple registry - O(1) per lookup/update
CONFIG = {}
def register_config(name, value):
"""Register configuration"""
globals()[f'config_{name}'] = value # O(1)
def get_config(name):
"""Get configuration"""
return globals().get(f'config_{name}') # O(1)
# Usage
register_config('timeout', 30) # O(1)
register_config('retries', 3) # O(1)
timeout = get_config('timeout') # O(1)
Performance Considerations¶
Frequency of calls¶
# globals() returns same dict object - O(1) each call
for i in range(1000):
d = globals() # O(1) - returns same dict reference
value = d['x']
# Caching is not necessary for globals(), but good for clarity
g = globals() # O(1)
for i in range(1000):
value = g['x'] # O(1) each
Large Namespaces¶
# globals() remains O(1) regardless of namespace size
# Iterating over the returned dict is O(n)
for i in range(10000):
globals()[f'var_{i}'] = i # O(1) each
# Still O(1): returns the same global dict object
g = globals()
# Iteration scales with number of entries - O(n)
names = [k for k in g if k.startswith("var_")]
# Prefer dict for custom data
my_data = {}
for i in range(10000):
my_data[f'var_{i}'] = i # O(1) each
Version Notes¶
- Python 2.x: Same behavior
- Python 3.x: Same behavior
- All versions:
globals()is O(1);locals()semantics are scope- and implementation-dependent