The locals() built-in function updates and returns a dictionary representing the current local symbol table.
Local Scope Introspection & Fast-Locals: At module top-level, locals() returns the module dictionary identical to globals(). Inside functions, however, locals() returns a snapshot dictionary derived from the function call frame. Modifying this dictionary inside a function does not modify local variables because CPython stores local variables in optimized array slots. To inspect object attribute mappings, see vars(). When evaluating expressions with isolated scopes, see eval() and exec(), or inspect object representations with repr().
The locals() function takes no arguments and returns a mapping of the current local namespace:
# Formal syntax of built-in locals()
local_namespace = locals()
| Property | Value | Description & Operational Rule |
|---|---|---|
| Arguments | None |
locals() takes zero arguments. Passing parameters raises TypeError. |
| Return Type | dict |
Returns a dictionary mapping variable names to their bound objects. |
| Module Behavior | Identical to globals() |
At module scope, locals() is globals() evaluates to True. |
| Function Behavior | Read-Only Snapshot | Inside functions, modifying the returned dictionary does not modify variables. |
The behavior of locals() changes fundamentally based on where it is called:
# 1. Module level vs. Function level namespace
module_var = "plus2net"
# At module top-level, locals() is identical to globals()
print("Module level locals() is globals():", locals() is globals())
def calculate_enrollment(students, capacity=50):
available_seats = capacity - students
# Inside a function, locals() returns only local scope symbols
current_locals = locals()
print("Function local variables:")
for param, value in current_locals.items():
print(f" {param:<16}: {value}")
return available_seats
remaining = calculate_enrollment(38)
print("Remaining seats: ", remaining)Terminal Output:
Module level locals() is globals(): True
Function local variables:
students : 38
capacity : 50
available_seats : 12
Remaining seats: 12
A classic beginner pitfall is attempting to modify local variables by assigning to locals() inside a function. In CPython, function local variables are stored in an optimized C array indexed by integers (via bytecode instructions LOAD_FAST and STORE_FAST). Calling locals() constructs a temporary dictionary copy from that array, so mutating the dictionary leaves the underlying variable untouched:
# 2. The local mutation trap: why updating locals() fails
def test_mutation():
greeting = "plus2net"
print("Initial greeting: ", greeting)
# Attempting to mutate local variable through locals()
locals()["greeting"] = "Welcome"
print("After locals() update:", greeting)
test_mutation()Terminal Output:
Initial greeting: plus2net
After locals() update: plus2net
Inside a class definition, locals() captures attributes before the class object is compiled:
# 3. locals() inside class definition blocks
class CourseProfile:
course_name = "Python Metaprogramming"
difficulty = "Advanced"
# At class body level, locals() captures class attributes
class_symbols = locals()
print("Class body locals keys:", [k for k in class_symbols if not k.startswith("__")])
def __init__(self, course_id, enrolled):
self.course_id = course_id
self.enrolled = enrolled
# Inside method, locals() captures method parameters
print("Method locals: ", {k: v for k, v in locals().items() if k != "self"})
profile = CourseProfile("PY-401", 125)Terminal Output:
Class body locals keys: ['course_name', 'difficulty']
Method locals: {'course_id': 'PY-401', 'enrolled': 125}
Side-by-side comparison across scope inspection tools:
| Feature | locals() |
globals() |
vars() |
|---|---|---|---|
| Target Scope | Current local function namespace | Current module global namespace | Object __dict__ or current locals |
| Direct Mutability | Snapshot only (inside functions) | Yes (modifies module variables) | Yes if object __dict__ is writable |
| Module-Level Role | Identical to globals() |
Module symbol table reference | Identical to locals() if no argument |
A concise pattern is passing locals() directly to string formatting methods to avoid manually unpacking variables:
# 4. Production Recipe: Dynamic logging and string formatting
def generate_student_report(name, subject, score, total=100):
percentage = (score / total) * 100
status = "Passed" if percentage >= 60 else "Failed"
# Use locals() mapping directly for clean template formatting
report = "{name} - {subject}: {score}/{total} ({percentage:.1f}%) - Status: {status}".format(**locals())
return report
print(generate_student_report("Alex", "Python Basics", 92))
print(generate_student_report("Sam", "Data Structures", 54))Terminal Output:
Alex - Python Basics: 92/100 (92.0%) - Status: Passed
Sam - Data Structures: 54/100 (54.0%) - Status: Failed
Calling exec() inside a function without explicit dictionaries cannot modify local fast variables:
# 5. Trap: exec() inside functions with locals()
def execute_dynamic():
x = 10
# exec() cannot modify local fast variables without explicit dict
exec("x = 99")
print("Local x after exec():", x)
# Correct approach: pass an explicit namespace dictionary
custom_local = {}
exec("y = 99", {}, custom_local)
print("Safely extracted y: ", custom_local["y"])
execute_dynamic()Terminal Output:
Local x after exec(): 10
Safely extracted y: 99
Explore local namespace snapshots, immutability mechanics, and template formatting in our interactive companion Jupyter Notebook:
Explore interactive code cells, test local mutation behaviors, inspect class body namespaces, and run self-validating assertions.
View on GitHub Run in Google ColabRun these unit checks directly in Python or your local environment to verify locals() mechanics:
# 6. Self-verifying unit test suite for locals()
# Test 1: Module-level equivalence
assert locals() is globals(), "At module level locals() must be globals()"
# Test 2: Function local extraction
def demo(a, b=20):
c = a + b
return locals()
loc = demo(10)
assert loc["a"] == 10 and loc["b"] == 20 and loc["c"] == 30
# Test 3: Immutability check inside functions
def check_immutable():
val = "original"
locals()["val"] = "modified"
return val
assert check_immutable() == "original", "locals() mutation must not affect variable"
# Test 4: String formatting pattern
name, score = "Alex", 95
summary = "{name}: {score}".format(**locals())
assert summary == "Alex: 95"
print("All locals() unit assertions successfully verified!")Terminal Output:
All locals() unit assertions successfully verified!
CPython uses optimized array slots (fast-locals) for function variables. locals() creates a temporary dictionary copy rather than exposing the real array.
At the top level of a module or script outside of any function or class, locals() is globals() evaluates to True.
Yes. Calling locals() immediately at the start of a function returns a dictionary mapping all parameter names to their received argument values.
Called without arguments, vars() behaves like locals(). Called with an object, vars(obj) returns that object's __dict__.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.