I'm using an exec() statement to set a value, like so:
foo = 3
def return_4():
return 4
instruction = 'foo = return_4()'
exec(instruction) # <---- WHERE THE MAGIC HAPPENS
print(foo)
This comes out as 4, as I expect.
My program has operations for manipulating a Rubik's cube. In this stripped down version, I'll do four things:
I'll instantiate a cube, filling in one face (with abbreviations for 'front top left' and 'front bottom right' and the like).
I'll have a function that rotates that front face.
I'll have an 'interpreter' function, which takes a cube and a list of instructions, and applies those instructions to the cube, returning the modified cube. Here's where I use 'exec' (and where I think the breakage happens).
Finally, I'll run the interpreter on my partial cube with the instruction to rotate the face one time.
+
my_cube = [['FTL', 'FTM', 'FTR',
'FML', 'FMM', 'FMR',
'FBL', 'FBM', 'FBR'],
[],[],[],[],[]] # other faces specified in actual code
def rotate_front(cube):
front = cube[0]
new_front = [front[6],front[3],front[0],
front[7],front[4],front[1],
front[8],front[5],front[2]]
# ...
ret_cube = cube
ret_cube[0] = new_front
# pdb says we are returning a correctly rotated cube,
# and calling this directly returns the rotated cube
return ret_cube
def process_algorithm(cube=default_cube, algorithm=[]):
return_cube = cube
for instruction in algorithm:
exec('return_cube = ' + instruction + '(return_cube)') # <--- NO MAGIC!
# ACCORDING TO pdb, return_cube HAS NOT BEEN ROTATED!
return return_cube
process_algorithm(cube = my_cube, algorithm = ['rotate_front'])
If I replace the exec(x = y) formation with x = eval(y), it seems to work. return_cube = eval(instruction + '(return_cube)')
So maybe this is just academic. Why does the toy example work, and the actual code fail? (Am I doing something obvious and silly, like missing an equals sign? I'm going to kick myself, I bet...)
Thanks for any help anyone can offer.
On Python 2.x, exec
was a statement that changed variable lookup from LOAD_GLOBAL
and LOAD_FAST
to LOAD_NAME
to every name you access on your function.
That means it first search the local scope to see if can find the name to after check the global scope.
Now, on Python 3.x, the exec
function cannot change this lookup and will never find the name you defined unless you add an argument with the
scope you want the result to be evaluated.
exec(some_code, globals())
For that to work, you need to add global my_var
inside the function to make sure the lookup will work.
Keep in mind these things will be inserted in the global namespace of your module...
BTW, why do you need exec
or eval
? Why can't you add real functions to your algorithm
list?
As side note, I can see you don't change the algorithm
var on your function, but if you do it'll introduce some undesired side effects because the default value you created is mutable and will be used on all function calls.
For safety, change it to None
and create a new list if needed.