Get a dict of all variables currently in scope and their values

Question:

Consider this snippet:

globalVar = 25

def myfunc(paramVar):
    localVar = 30
    print "Vars: {globalVar}, {paramVar}, {localVar}!".format(**VARS_IN_SCOPE)

myfunc(123)

Where VARS_IN_SCOPE is the dict I’m after that would contain globalVar, paramVar and localVar, among other things.

I’d like to basically be able to reference all the variables that are currently in scope inside the string. Hence the expected output would be:

Vars: 25, 123, 30

I can achieve this by passing **dict(globals().items() + locals().items()) to format(). Is this always correct or are there some corner cases that this expression would handle incorrectly?

Rewritten to clarify the question.

Asked By: Roman Starkov

||

Answers:

Does this do what you intended?

d = dict(globals())
d.update(locals())

If I read the documentation correctly, you create a copy of the globals() dict, then you overwrite any duplicates and insert new entries from the locals() dict (since the locals() should have preference within your scope, anyway).


I haven’t had any luck in getting a proper function to return the full dictionary of variables in scope of the calling function. Here’s the code (I only used pprint to format the output nicely for SO):

from pprint import *

def allvars_bad():
    fake_temp_var = 1
    d = dict(globals())
    d.update(locals())
    return d

def foo_bad():
    x = 5
    return allvars_bad()

def foo_good():
    x = 5
    fake_temp_var = "good"
    d = dict(globals())
    d.update(locals())
    return d

pprint (foo_bad(), width=50)
pprint (foo_good(), width=50)

and the output:

 {'PrettyPrinter': <class pprint.PrettyPrinter at 0xb7d316ec>,
 '__builtins__': <module '__builtin__' (built-in)>,
 '__doc__': None,
 '__file__': 'temp.py',
 '__name__': '__main__',
 '__package__': None,
 'allvars_bad': <function allvars_bad at 0xb7d32b1c>,
 'd': <Recursion on dict with id=3084093748>,
 'fake_temp_var': 1,
 'foo_bad': <function foo_bad at 0xb7d329cc>,
 'foo_good': <function foo_good at 0xb7d32f0c>,
 'isreadable': <function isreadable at 0xb7d32c34>,
 'isrecursive': <function isrecursive at 0xb7d32c6c>,
 'pformat': <function pformat at 0xb7d32bc4>,
 'pprint': <function pprint at 0xb7d32b8c>,
 'saferepr': <function saferepr at 0xb7d32bfc>}
{'PrettyPrinter': <class pprint.PrettyPrinter at 0xb7d316ec>,
 '__builtins__': <module '__builtin__' (built-in)>,
 '__doc__': None,
 '__file__': 'temp.py',
 '__name__': '__main__',
 '__package__': None,
 'allvars_bad': <function allvars_bad at 0xb7d32b1c>,
 'd': <Recursion on dict with id=3084093884>,
 'fake_temp_var': 'good',
 'foo_bad': <function foo_bad at 0xb7d329cc>,
 'foo_good': <function foo_good at 0xb7d32f0c>,
 'isreadable': <function isreadable at 0xb7d32c34>,
 'isrecursive': <function isrecursive at 0xb7d32c6c>,
 'pformat': <function pformat at 0xb7d32bc4>,
 'pprint': <function pprint at 0xb7d32b8c>,
 'saferepr': <function saferepr at 0xb7d32bfc>,
 'x': 5}

Note that in the second output, we have overwritten fake_temp_var, and x is present; the first output only included the local vars within the scope of allvars_bad.

So if you want to access the full variable scope, you cannot put locals() inside another function.


I had suspected there was some sort of frame object, I just didn’t (know where to) look for it.

This works to your spec, I believe:

def allvars_good(offset=0):
    frame = sys._getframe(1+offset)
    d = frame.f_globals
    d.update(frame.f_locals)
    return d


def foo_good2():
    a = 1
    b = 2
    return allvars_good()

–>

{'PrettyPrinter': <class pprint.PrettyPrinter at 0xb7d6474c>,
 '__builtins__': <module '__builtin__' (built-in)>,
 '__doc__': None,
 '__file__': 'temp.py',
 '__name__': '__main__',
 '__package__': None,
 'a': 1,
 'allvars_bad': <function allvars_bad at 0xb7d65b54>,
 'allvars_good': <function allvars_good at 0xb7d65a04>,
 'b': 2,
 'foo_bad': <function foo_bad at 0xb7d65f44>,
 'foo_good': <function foo_good at 0xb7d65f7c>,
 'foo_good2': <function foo_good2 at 0xb7d65fb4>,
 'isreadable': <function isreadable at 0xb7d65c6c>,
 'isrecursive': <function isrecursive at 0xb7d65ca4>,
 'pformat': <function pformat at 0xb7d65bfc>,
 'pprint': <function pprint at 0xb7d65bc4>,
 'saferepr': <function saferepr at 0xb7d65c34>,
 'sys': <module 'sys' (built-in)>}
Answered By: Mark Rushakoff

You could make your own:

allvars = dict()
allvars.update(globals())
allvars.update(locals())

or combine the first two lines:

allvars = dict(globals())
allvars.update(locals())
Answered By: Ned Batchelder

Interpolation into strings works in the simplest possible way. Just list your variables. Python checks locals and globals for you.

globalVar = 25

def myfunc(paramVar):
    localVar = 30
    print "Vars: %d, %d, %d!" % ( globalVar, paramVar, localVar )

myfunc(123)
Answered By: S.Lott

Best way to merge two dicts as you’re doing (with locals overriding globals) is dict(globals(), **locals()).

What the approach of merging globals and locals is missing is (a) builtins (I imagine that’s deliberate, i.e. you don’t think of builtins as “variables”… but, they COULD be, if you so choose!-), and (b) if you’re in a nested function, any variables that are local to enclosing functions (no really good way to get a dict with all of those, plus — only those explicitly accessed in the nested function, i.e. “free variables” thereof, survive as cells in a closure, anyway).

I imagine these issues are no big deal for your intended use, but you did mention “corner cases”;-). If you need to cover them, there are ways to get the built-ins (that’s easy) and (not so easy) all the cells (variables from enclosing functions that you explicitly mention in the nested function — thefunction.func_code.co_freevars to get the names, thefunction.func_closure to get the cells, cell_contents on each cell to get its value). (But, remember, those will only be variables from enclosing functions that are explicitly accessed in your nested function’s code!).

Answered By: Alex Martelli
globalVar = 25

def myfunc(paramVar):
    localVar = 30
    all_vars = locals.copy()
    all_vars.update(globals())
    print "Vars: {globalVar}, {paramVar}, {localVar}!".format(all_vars)

myfunc(123)
Answered By: Evan Fosmark

Python 3.5 or greater:

globalVar = 25

def myfunc(paramVar):
    localVar = 30
    print("Vars: {globalVar}, {paramVar}, {localVar}!".format(**{**locals(), **globals()}))

myfunc(123)
Answered By: Nic Scozzaro
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.