In Python 2 (not sure about 3), the locals dictionary only gets updated when you actually call locals(). So e.g.
l=locals()
x=2
l['x']
fails because l
doesn't have the key "x" in it, but
l=locals()
x=2
locals()
l['x']
returns 2.
I'm looking for a way to force an update of the locals dictionary, but the trick is that I'm in a different stack frame. So e.g. I'm looking to do
l=locals()
x=2
force_update()
l['x']
and I need to write the force_update()
function. I know that from said function I can get the parent frame via inspect.currentframe().f_back
, and even the parent (non-updated) locals via inspect.currentframe().f_back.f_locals
, but how can I force an update?
If this seems convoluted, my main goal is to write a function which is shorthand for "{some} string".format(**dict(globals(),**locals()))
so I don't have to type that out each time, and can instead do fmt("{some} string")
. Doing so I run into the issue above.
Edit: With Martjin answer below, below is essentially the solution I was looking for. One could play around with exactly how they get the stack frame of the callee, here I do it via partial
.
from functools import partial
from inspect import currentframe
fmt = partial(lambda s,f: s.format(**dict(globals(),**f.f_locals)),f=currentframe())
x=2
print fmt("{x}") #prints "2"
Simply accessing f_locals
on a frame object triggers the copy, so using inspect.currentframe().f_back.f_locals
is enough.
See the frame_getlocals()
function in the frameobject.c
implementation:
static PyObject *
frame_getlocals(PyFrameObject *f, void *closure)
{
PyFrame_FastToLocals(f);
Py_INCREF(f->f_locals);
return f->f_locals;
}
PyFrame_FastToLocals
is the function used to copy the data from the interal array tracking locals values to a dictionary. frame_getlocals
is used to implement the frame.f_locals
descriptor (a property); see the frame_getsetlist
definition.
The PyFrame_FastToLocalsWithError
function used above is exactly what locals()
uses to produce the same dictionary (by wrapping the PyEval_GetLocals
function).