Get locals from calling namespace in Python
Asked Answered
E

3

53

I want to retrieve the local variables from Python from a called function. Is there any way to do this? I realize this isn't right for most programming, but I am basically building a debugger. For example:

def show_locals():
  # put something in here that shows local_1.

local_1 = 123
show_locals()  # I want this to show local_1.

What do I put in the body of show_locals? If I have to modify the calling statement, what is the minimal modification I can make?

Note: this must work when show_locals is in a different module to its caller.

Endoskeleton answered 8/7, 2011 at 0:47 Comment(0)
N
95

If you're writing a debugger, you'll want to make heavy use of the inspect module:

def show_callers_locals():
    """Print the local variables in the caller's frame."""
    import inspect
    frame = inspect.currentframe()
    try:
        print(frame.f_back.f_locals)
    finally:
        del frame
Niphablepsia answered 8/7, 2011 at 0:52 Comment(9)
Yay! I found your solution after running into significant performance issues with an alternative solution: caller = inspect.stack()[1][0]; myvars = caller.f_locals. Your way is MUCH faster.Diplomate
Why is deleting the frame in the finally block necessary? Is keeping the frame object in memory somehow expensive? This is a minor point, of course, but it left me confused. Also, thank you, this is very helpful!Kava
The inspect documentation explains the del frame.Niphablepsia
@Kava and Gareth, it seems to me the del frame (and thus the try block) is actually a no-op, since the frame variable is about to go out of scope anyway. No?Epiphora
@DonHatch: See my comment of 2016-11-02.Niphablepsia
Yes, i see your comment referring to the example in the inspect documentation, and I see that that example has del frame, but that does not address my question.Epiphora
@DonHatch: Did you read the note in the documentation I linked to? (The whole note, not just the example.)Niphablepsia
Yes. If I understand it correctly, it's suggesting that you can help the garbage collector by explicitly clearing the variable so that it the garbage collector doesn't need to rely on cycle detection. Some ways of doing that are: del frame, or frame = None, or just let the variable go out of scope. In your case the variable is about to go out of scope anyway, so there's no point in doing either of those other things.Epiphora
Ah wait, I get it. "go out of scope" means, literally, that the stack frame that owns the local variable is about to be released. But, 'frame' points to exactly that stack frame. That's the cycle they are talking about. Ok, I concede the del frame is useful. OTOH I'm not sure the variable is necessary at all. I think the function body could be print(inspect.currentframe().f_back.f_locals)Epiphora
P
7

Perhaps it is worth pointing out that the technique from the accepted answer that reads from the caller's stack frame:

import inspect
def read_from_caller(varname):
    frame = inspect.currentframe().f_back
    try:
        v = frame.f_locals[varname]
        return v
    finally:
        del frame

can also write into the caller's namespace:

import inspect
def write_in_caller(varname, v):
    frame = inspect.currentframe().f_back
    try:
        frame.f_locals[varname] = v
    finally:
        del frame

If you put that in a module called "access_caller", then

import access_caller
access_caller.write_in_caller('y', x)

is an elaborate way of writing

y = x

(I am writing this as a fresh answer because I don't have enough reputation points to write a comment.)

Participation answered 13/7, 2020 at 22:21 Comment(2)
Hi @daniel, it seems it does not work in python3.8 at least. The setitem call on f_locals has no effectAdministrator
Hi @grégoire-roussel, Can you share a code example? For me, writing "x = 45", then "write_in_caller('y', x)", then "print(y)" reports 45 as expected. I am using python 3.8.5 in linux.Participation
W
0

You use the python builtin, dir() or vars():

vars(object)

For examples using dir(), see: this post

Examples using vars:

>>> class X:
...     a=1
...     def __init__(self):
...         b=2
... 
>>> 
>>> vars(X)
{'a': 1, '__module__': '__main__', '__doc__': None, '__init__': <function __init__ at 0x100488848>}
>>> 
>>> vars(X())
{}

A potentially problematic fact: New style classes not return the same result

>>> class X(object):
...     a=1
...     def __init__(self):
...         b=2
... 
>>> 
>>> vars(X)
<dictproxy object at 0x1004a1910>
>>> vars(X())
{}

Also: for an instantiated class (new and old style), if you add a variable after instantiating, vars will return the object's dict like this:

>>> x = X() 
>>> x.c = 1
>>> vars(x)
{'c': 1}
>>> 

See: http://docs.python.org/library/functions.html#vars

Warmth answered 8/7, 2011 at 1:36 Comment(1)
This doesn't answer the question, the OP wants the oppersite where inside of func_1, if you call func_2 you can see all the locals from func_1.Saintsimonianism

© 2022 - 2024 — McMap. All rights reserved.