Any way to modify locals dictionary?

locals is a built in function that returns a dictionary of local values. The documentation says:

Warning

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

Unfortunately, exec has the same problem in Python 3.0. Is there any way round this?

Use Case

Consider:

@depends("a", "b", "c", "d", "e", "f")
def test():
    put_into_locals(test.dependencies)

depends stores the strings provided in its arguments in a list test.dependences. These strings are keys in a dictionary d. I would like to be able to able to write put_into_locals so that we could pull the values out of d and put them into the locals. Is this possible?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

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.

Method 2

The local variables are modified by assignment statements.

If you have dictionary keys which are strings, please don’t also make them local variables — just use them as dictionary keys.

If you absolutely must have local variables do this.

def aFunction( a, b, c, d, e, f ):
    # use a, b, c, d, e and f as local variables

aFunction( **someDictWithKeys_a_b_c_d_e_f )

That will populate some local variables from your dictionary without doing anything magical.

Method 3

This isn’t possible. I think this is to allow for performance optimizations later on. Python bytecode references locals by index, not by name; if locals() was required to be writable, it could prevent interpreters from implementing some optimizations, or make them more difficult.

I’m fairly certain you’re not going to find any core API that guarantees you can edit locals like this, because if that API could do it, locals() wouldn’t have this restriction either.

Don’t forget that all locals must exist at compile-time; if you reference a name that isn’t bound to a local at compile-time, the compiler assumes it’s a global. You can’t “create” locals after compilation.

See this question for one possible solution, but it’s a serious hack and you really don’t want to do that.

Note that there’s a basic problem with your example code:

@depends("a", "b", "c", "d", "e", "f")
def test():
    put_into_locals(test.dependencies)

"test.dependencies" isn’t referring to “f.dependencies” where f is the current function; it’s referencing the actual global value “test”. That means if you use more than one decorator:

@memoize
@depends("a", "b", "c", "d", "e", "f")
def test():
    put_into_locals(test.dependencies)

it’ll no longer work, since “test” is memoize’s wrapped function, not depends’s. Python really needs a way to refer to “the currently-executing function” (and class).

Method 4

I would store it in a variable:

refs    = locals()
def set_pets():
    global refs
    animals = ('dog', 'cat', 'fish', 'fox', 'monkey')
    for i in range(len(animals)):
        refs['pet_0%s' % i] = animals[i]

set_pets()
refs['pet_05']='bird'
print(pet_00, pet_02, pet_04, pet_01, pet_03, pet_05 )
>> dog fish monkey cat fox bird

And if you want to test your dict before putting it in locals():

def set_pets():
    global refs
    sandbox = {}
    animals = ('dog', 'cat', 'fish', 'fox', 'monkey')
    for i in range(len(animals)):
        sandbox['pet_0%s' % i] = animals[i]
    # Test sandboxed dict here
    refs.update( sandbox )

Python 3.6.1 on MacOS Sierra

Method 5

I’m not sure if it is subject to the same restrictions, but you can get a direct reference to the current frame (and from there, the local variables dictionary) through the inspect module:

>>> import inspect
>>> inspect.currentframe().f_locals['foo'] = 'bar'
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'foo', 'inspect']
>>> foo
'bar'


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x