Any Way to Modify Locals Dictionary

Any way to modify locals dictionary?

I just tested exec and it works in Python 2.6.2

>>> def test():
... exec "a = 5"
... print a
...
>>> test()
5

If you are using Python 3.x, it does not work anymore because locals are optimized as an array at runtime, instead of using a dictionary.

When Python detects the "exec statement", it will force Python to switch local storage from array to dictionary. However since "exec" is a function in Python 3.x, the compiler cannot make this distinction since the user could have done something like "exec = 123".

http://bugs.python.org/issue4831

To modify the locals of a function on
the fly is not possible without
several consequences: normally,
function locals are not stored in a
dictionary, but an array, whose
indices are determined at compile time
from the known locales. This collides
at least with new locals added by
exec. The old exec statement
circumvented this, because the
compiler knew that if an exec without
globals/locals args occurred in a
function, that namespace would be
"unoptimized", i.e. not using the
locals array. Since exec() is now a
normal function, the compiler does not
know what "exec" may be bound to, and
therefore can not treat is specially.

Update locals from inside a function

The locals are not updated here because, in the first case, the variable declared has a global scope. But when declared inside a function, the variable loses scope outside it.

Thus, the original value of the locals() is not changed in the UpdateLocals function.

PS: This might not be related to your question, but using camel case is not a good practice in Python. Try using the other method.

update_locals() instead of UpdateLocals()

Edit To answer the question in your comment:

There is something called a System Stack. The main job of this system stack during the execution of a code is to manage local variables, make sure the control returns to the correct statement after the completion of execution of the called function etc.,

So, everytime a function call is made, a new entry is created in that stack,
which contains the line number (or instruction number) to which the control has to return after the return statement, and a set of fresh local variables.

The local variables when the control is inside the function, will be taken from the stack entry. Thus, the set of locals in both the functions are not the same. The entry in the stack is popped when the control exits from the function. Thus, the changes you made inside the function are erased, unless and until those variables have a global scope.

Modify *existing* variable in `locals()` or `frame.f_locals`

It exists an undocumented C-API call for doing things like that:

PyFrame_LocalsToFast

There is some more discussion in this PyDev blog post. The basic idea seems to be:

import ctypes

...

frame.f_locals.update({
'a': 'newvalue',
'b': other_local_value,
})
ctypes.pythonapi.PyFrame_LocalsToFast(
ctypes.py_object(frame), ctypes.c_int(0))

I have yet to test if this works as expected.

Note that there might be some way to access the Fast directly, to avoid an indirection if the requirements is only modification of existing variable. But, as this seems to be mostly non-documented API, source code is the documentation resource.

How can I force update the Python locals() dictionary of a different stack frame?

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

locals().update(dictionary) doesn't add all the variables

That's the expected behaviour, by the docs:

The contents of this dictionary should not be modified; changes may not affect the values of local and free variables used by the interpreter.

I think, one of the reasons for that is that whether a variable is global or local is defined during the function compilation, so that in:

def func():
locals()['val'] = 1
print val

the last statement always reads from the global variable, since the local variable is not declared. So, ability to add locals dynamically would make life harder.

Why is it bad idea to modify locals in python?

Modification is a bad idea because the documentation (which you link) explicitly says not to:

Note: The contents of this dictionary should not be modified; changes may not affect the values of local and free variables used by the interpreter.

You don't need any more reason than that.

If you are using it in a way that doesn't modify any variables, then you'll be fine, but I'd question the design and see if there's a better way to do what you want.


In the specific example you link, locals is actually globals(), as you use it in the global scope of a module. This very specific use works now and, though I expect it to continue to work just as with globals, you might as well just use globals instead.

An even cleaner solution is probably, without knowing the rest of your design, to use a regular ol' dictionary for your variables; then use data["x"] = value instead of globals()["x"] = value.

How to update local variables in function with a dictionary?

You can't create local variables programmatically in that way.

Instead, just use the dictionary directly by accessing its keys.

How to change dictionary outside the funchion, if I redefine it inside?

Simply do a x.update method on your dic like:

def Teser(Test):
Test.update({'Hell':'No'})


Related Topics



Leave a reply



Submit