Python doesn’t expose raw memory the way lower-level languages do, but you can still track how and when your variables change. In this tutorial, we’ll build a lightweight memory tracing tool using Python’s built-in sys.settrace function, allowing us to monitor variable assignments and get insights into runtime behavior — no C extensions, no third-party tools.
Setup
This method works in pure Python 3. No dependencies needed. You’ll just need a working Python environment and a target function you want to introspect. It's particularly useful for debugging, reverse engineering, or building your own lightweight instrumentation tools.
Full Working Code
import sys
def trace_vars(frame, event, arg):
    if event != "line":
        return trace_vars
code = frame.f_code
lineno = frame.f_lineno
locals_now = frame.f_locals.copy()
global last_locals
if code.co_name not in last_locals:
    last_locals[code.co_name] = locals_now
    return trace_vars
old_locals = last_locals[code.co_name]
for var, new_val in locals_now.items():
    if var not in old_locals:
        print(f"[{code.co_name}:{lineno}] NEW {var} = {new_val}")
    elif old_locals[var] != new_val:
        print(f"[{code.co_name}:{lineno}] MODIFIED {var}: {old_locals[var]} → {new_val}")
for var in old_locals:
    if var not in locals_now:
        print(f"[{code.co_name}:{lineno}] DELETED {var}")
last_locals[code.co_name] = locals_now
return trace_vars
    
    
def monitor(func):
    def wrapper(*args, **kwargs):
        global last_locals
        last_locals = {}
        sys.settrace(trace_vars)
        try:
            return func(*args, **kwargs)
        finally:
            sys.settrace(None)
    return wrapper
  
  
  Example usage
@monitor
def run_example():
    a = 10
    b = a + 5
    b = b * 2
    del a
    return b
run_example()
Explanation
By using sys.settrace, we register a line-by-line callback that can introspect the current local variables at each step. We store a snapshot of the locals for each function, then compare it on the next invocation to detect changes — additions, updates, deletions. It’s a powerful (and often overlooked) way to understand the control and data flow of any Python function at runtime.
Pros & Cons
✅ Pros
- Works with unmodified Python
- Fully userland — no C or unsafe operations
- Easy to wrap around any function
- Valuable for introspection, testing, and education
⚠️ Cons
- Significant performance overhead
- Doesn’t work well with async or multithreaded code
- Can’t access native extensions or memory directly
- Not suitable for production monitoring
Summary
This approach gives you a window into the lifecycle of variables in Python, letting you trace changes without any third-party packages or tooling. While it's not a true memory inspector, it's a powerful pattern for anyone debugging tricky code or trying to understand execution in fine-grained detail.
If this was useful, you can Buy Me a Coffee ☕
 
 
    
Top comments (0)