How can I use exec in functions in python3.6 similar to python2.7?

Hello,

in python 2.7 the following code works:

def f( b ):
… exec( ‘a=%s’%b )
… print ‘a=’, a

f( 1 )
a= 1

in python3.6 this works in the interpreter:

b=1
exec(‘a=%s’%b)
a
1

but not in a function:

def f( b ):
… exec( ‘a=%s’%b )
… print( ‘a=’, a )

f( 1 )
Traceback (most recent call last):
File “”, line 1, in
File “”, line 3, in f
NameError: name ‘a’ is not defined

How can I get the latter working?

Thanks for all advice!
Jan

I believe in this case the “a” is local to the exec, so the function cannot access it. You could probably use the scope systems on exec to override this and allow it to populate the global namespace.

def f( b ):
    exec( 'a=%s'%b, globals())
    print( 'a=', a )

f(1)

Hi Jan,

Unfortunately you can’t. The behaviour of exec inside functions has
changed in Python 3. You could try alternative implementations of the
interpreter such as PyPy and Nuitka, but the chances aren’t good that
they will support it either.

To understand why the behaviour changed, and why you can’t get it to
work now, I have to talk about how variables are stored in Python.

You probably know that variables in Python are stored in a dict. You can
access that dict through globals() and locals(), right?

a = 1
locals()['a'] = 2
print(a)  # prints 2

Well, sort of. That works in top-level module code, like in the
interactive interpreter, and at the top level of classes, but not inside
functions. If you try that code inside a function, even in Python 2, you
will see a has the value 1, not 2.

There’s even a documented note under locals:

https://docs.python.org/2/library/functions.html#locals

The thing is, CPython uses a special mechanism for function local
variables to speed them up. Inside a function, the dict returned by
locals() isn’t the actual namespace used for variables, but a copy.

In Python 2, using exec() inside the function switched on a weird hybrid
behaviour where the function would use both the extra-fast local
variables and the slower dict-based namespace. This code was complex,
complicated, confusing, hard to maintain, and sometimes would behave in
odd ways that was hard to understand.

So it was removed in Python 3, which means that you can no longer use
exec to modify function local variables. (It will still work if the
variable is a global.)

https://docs.python.org/3/library/functions.html#exec

The only work around is something like this:

def function(arg):
    mylocals = {}
    exec('a = %s + 1' % arg, globals(), mylocals)
    a = mylocals['a']
    print(a)

You can use mylocals = locals() to initialise values in your exec
code, like this:

def function(arg):
    mylocals = locals()
    exec('a = arg + 1', globals(), mylocals)
    a = mylocals['a']
    print(a)

but any changes to locals() will not affect the real local variables.
The only way to change their actual value, or create new local
variables, is with an actual assignment.

If you show us what you’re doing with exec, we might be able to suggest
a better, cleaner way.