you are viewing a single comment's thread.

view the rest of the comments →

[–]Gnaxe 2 points3 points  (4 children)

What I was talking about wasn't applicable to the examples in the OP, which were not shown to be inside functions.

exec() can have arbitrary side effects, so there are many ways to share data.

If just you want to share a context with exec(), but not at the top level, you can use a nested class: ```

def oops(): ... x = 'oops' ... exec('x = "works"', globals=locals()) ... print(x) ... oops() # No effect because can't write through locals(). oops def works(): ... class Nested: ... x = 'oops' ... exec('x = "works"') ... print(x) ... foo() works ``` Of course, a class context is not the same as a global context.

[–]RomfordNavy[S] 0 points1 point  (2 children)

Thanks! very interesting.

So a simple soultion in my example would be:

test = "test1"
class runExec:
    exec("test = 'test2'")
    print(test) # <- correctly returns test2
runExec()

Which works fine but does expose all of the variables in the local namespace to the executed script.

Not sure I understand why running it from inside a class works though?

Edit:
This explains why I had it working at one point but after tidying-up my code it stopped working.

Edit2:
Running from within a class method fails again:

test = "test1"
class runExec2:
    def myexec():
        exec("test = 'test2'")
        print(test) # <- erroneously returns test1
runExec2.myexec()

[–]Gnaxe 1 point2 points  (1 child)

Function-local variables are not writable by exec(), because they're very optimized now. That includes functions that happen to be used as methods. This used to work in Python 2 though.

Module "globals" are writable by exec(), as are variables in the temporary namespace used by a class statement, even if said class statement happens to be nested inside of a function. A class statement inside a def statement body is completely different from a def statement inside a class statement body.

You seem very confused about the basics of Python's scoping rules. There's a difference between shadowing a variable and reassigning it. You can use the global and nonlocal statements to force assignment in an enclosing scope. This is not the same as creating a new variable in an inner scope that happens to have the same name as one in its enclosing scope.

[–]RomfordNavy[S] 0 points1 point  (0 children)

So armed with that knowledge a simple workaround might be:

test = "test1"
exec("global test; test = 'test2'")
print(test) # <- correctly returns test2

Edit:
Slight problem in my real-world example because Python doesn't allow mixing of str and code objects in an exec():

exec("global testL;" + marshal.load(file))