51

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.

Roman Starkov
  • 59,298
  • 38
  • 251
  • 324
  • Why would you want to do that? Chances are, there's a better way to do whatever you're trying to do. – Sasha Chedygov Jun 25 '09 at 00:39
  • I thought the snippet would make it clear. I'd like to interpolate the variables inside strings. By all means, there could be a better way. Please feel free to suggest one as an answer. – Roman Starkov Jun 25 '09 at 00:42
  • Sorry, the snippet does not explain **why** you need such a thing. Also, parameters are simply local -- it's not a separate scope. – S.Lott Jun 25 '09 at 01:05
  • Ok, you're right, rewritten to hopefully make it clearer. – Roman Starkov Jun 25 '09 at 01:30
  • Not much. You still don't explain why you want this, which makes it hard to tell you what the best way to do what you want is. I can only think of one situation, and that's if you want to do templating, in which case there already is a megaton of templating solutions for Python out there. – Lennart Regebro Jun 25 '09 at 09:25
  • Just passing `**locals()` is already kind of lazy, but at least the locals are easy to find. Passing in the globals, too just makes everyone look around the code for your values. Wide reliance on global variables, especially using them in odd contexts where they do not look like variable references at all, is generally bad for development. Copy the stuff local, or better yet just make things explicit. – Jon Jay Obermark Sep 01 '14 at 15:00
  • @JonJayObermark agreed in general about globals, but what I asked here was no different to `print "Foo is: %s" % foo`. You still need to "hunt" for what `foo` means in the current scope, and it could still be a local or a global. – Roman Starkov Sep 24 '14 at 13:36
  • Logically true, but going out of your way to spread trouble is seldom a good idea. Fetching something out of the global context into the local one makes it clear where it comes from and affords you the opportunity to give documentation, if only in the form of a more meaningful local name. – Jon Jay Obermark Sep 24 '14 at 16:09
  • Nowadays you can accomplish this with f-strings – wjandrea Jan 30 '20 at 01:10

6 Answers6

45

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!).

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • Weird that you can't get all the variables from inside a nested function. Anyone know why `locals()` won't work? Seems like it should! – sudo Jun 27 '16 at 21:06
8

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)>}
Mark Rushakoff
  • 249,864
  • 45
  • 407
  • 398
  • To answer your question, I don't know :) This is actually what I'd like to find out - whether just combining the two is the right thing to do. – Roman Starkov Jun 25 '09 at 00:51
  • Yeah, unfortunately locals() has to be right there. Similar with globals() - it must be in the same module to work. I wonder if some sys._getframe() trickery could be used instead. – Roman Starkov Jun 25 '09 at 01:43
2

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())
Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
2
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)
Evan Fosmark
  • 98,895
  • 36
  • 105
  • 117
1

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)
S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • That's not interpolation. http://en.wikipedia.org/wiki/Variable_(computer_science)#Interpolation -- Python doesn't have it as such, but with the .format() function you can get pretty close. – Roman Starkov Jun 25 '09 at 11:06
  • And in case you wonder why I dislike % - think about something like this: print "{path}/myscript.py --input {path}/file1 --output {path}/file2".format(**locals()) - with "%" I would need to pass the path three times. With lots of variables this becomes a complete nightmare to maintain. – Roman Starkov Jun 25 '09 at 11:23
  • @romkyns: That's not so. The string `%` operator can take a dict argument with no need to unpack it with `**` as `format` does: print "%(path)s/myscript.py --input %(path)s/file1 --output %(path)s/file2" % locals() – Don O'Donnell Dec 18 '09 at 08:36
0

Python 3.5 or greater:

globalVar = 25

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

myfunc(123)
Nic Scozzaro
  • 6,651
  • 3
  • 42
  • 46