Behavior of Exec Function in Python 2 and Python 3

Behavior of exec function in Python 2 and Python 3

There is a big difference between exec in Python 2 and exec() in Python 3. You are treating exec as a function, but it really is a statement in Python 2.

Because of this difference, you cannot change local variables in function scope in Python 3 using exec, even though it was possible in Python 2. Not even previously declared variables.

locals() only reflects local variables in one direction. The following never worked in either 2 or 3:

def foo():
a = 'spam'
locals()['a'] = 'ham'
print(a) # prints 'spam'

In Python 2, using the exec statement meant the compiler knew to switch off the local scope optimizations (switching from LOAD_FAST to LOAD_NAME for example, to look up variables in both the local and global scopes). With exec() being a function, that option is no longer available and function scopes are now always optimized.

Moreover, in Python 2, the exec statement explicitly copies all variables found in locals() back to the function locals using PyFrame_LocalsToFast, but only if no globals and locals parameters were supplied.

The proper work-around is to use a new namespace (a dictionary) for your exec() call:

def execute(a, st):
namespace = {}
exec("b = {}\nprint('b:', b)".format(st), namespace)
print(namespace['b'])

The exec() documentation is very explicit about this limitation:

Note: The default locals act as described for function locals() below: modifications to the default locals dictionary should not be attempted. Pass an explicit locals dictionary if you need to see effects of the code on locals after function exec() returns.

Getting Python2 exec behaviour in python3: UnboundLocalError: local variable

Python thinks ec is a local variable, which hasn't yet been assigned to, and you can't reference it before first assigning to it. Use the global statement at the start of your function, like so:

def function_with_exec(table):
# ec now refers to the global variable, which presumedly already been as
global ec
(sess, tgt) = 3, None
try:
exec("ec = some_object.{}_get_entry_count(sess_hdl=sess, dev_tgt=tgt)".
format(table))
except AttributeError:
return []

ec = some_function(ec)

See 7.12. The global statement

The global statement is a declaration which holds for the entire current code block. It means that the listed identifiers are to be interpreted as globals. It would be impossible to assign to a global variable without global, although free variables may refer to globals without being declared global.

You could also remove the call to exec by using getattr instead:

def function_with_exec():
global ec

(sess, tgt) = 3, None
try:
ec = getattr(some_object, f"{table}_get_entry_count")(sess_hdl=sess,
dev_tgt=tgt)
except AttributeError:
return []

ec = some_function(ec)

Why does the exec() function work differently inside and outside of a function?

Pass the current local scope to the function and use that as the local dictionary for exec().

# example 2
def example_function(loc):
string_two = 'greeting_two = "hi"'
exec(string_two, loc)
print(greeting_two)

example_function(locals())

Read more here

Behavior of exec function in Python 2 and Python 3

There is a big difference between exec in Python 2 and exec() in Python 3. You are treating exec as a function, but it really is a statement in Python 2.

Because of this difference, you cannot change local variables in function scope in Python 3 using exec, even though it was possible in Python 2. Not even previously declared variables.

locals() only reflects local variables in one direction. The following never worked in either 2 or 3:

def foo():
a = 'spam'
locals()['a'] = 'ham'
print(a) # prints 'spam'

In Python 2, using the exec statement meant the compiler knew to switch off the local scope optimizations (switching from LOAD_FAST to LOAD_NAME for example, to look up variables in both the local and global scopes). With exec() being a function, that option is no longer available and function scopes are now always optimized.

Moreover, in Python 2, the exec statement explicitly copies all variables found in locals() back to the function locals using PyFrame_LocalsToFast, but only if no globals and locals parameters were supplied.

The proper work-around is to use a new namespace (a dictionary) for your exec() call:

def execute(a, st):
namespace = {}
exec("b = {}\nprint('b:', b)".format(st), namespace)
print(namespace['b'])

The exec() documentation is very explicit about this limitation:

Note: The default locals act as described for function locals() below: modifications to the default locals dictionary should not be attempted. Pass an explicit locals dictionary if you need to see effects of the code on locals after function exec() returns.

What happend with exec in Python3.3?

Because exec() is now a function, you can no longer use it to set local names in Python functions.

In Python 2, where exec is a statement, the compiler could detect its use and disable the normal local name optimisations in place for functions.

Execute your code into a new dictionary instead:

namespace = {}
exec(logic, namespace)
l = namespace['Logic']()

Demo:

>>> logic = '''\
... class Logic:
... def Play(self, id, board):
... return id, board
... '''
>>> def Play(logic, board, id):
... namespace = {}
... exec(logic, namespace)
... l = namespace['Logic']()
... return l.Play(id, board)
...
>>> Play(logic, 'foo_board', 'bar_id')
('bar_id', 'foo_board')

Running exec inside function

It's going to damage your function's performance, as well as its maintainability, but if you really want to make your own code so much worse, Python2 (this will not work in Python3, there you need to use the second alternative) gives you "enough rope to shoot yourself in the foot" (;-):

>>> def horror():
... exec "x=23"
... return x
...
>>> print horror()
23

A tad less horrible, of course, would be to exec in a specific dict:

>>> def better():
... d = {}
... exec "x=23" in d
... return d['x']
...
>>> print better()
23

This at least avoids the namespace-pollution of the first approach.



Related Topics



Leave a reply



Submit