简体   繁体   中英

Modify Python global variable inside eval

I have code like this:

globals_defined = {'add': my_add_fn, 'divide': my_divide_fn}
eval_result = eval(<some code>, {data: {'name_1': 'NAME1', 'name_2': 'NAME2'}, globals_defined)

I would like set a global variable inside the eval and then be able to access it afterwards. So like:

globals_defined = {'add': my_add_fn, 'divide': my_divide_fn, count_iterations: 0}
eval_result = eval(<some code>, {data: {'name_1': 'NAME1', 'name_2': 'NAME2'}, globals_defined)
print 'iterations: ' + str(globals_defined['count_iterations']) 

And ideally that would print a modified value of count_iterations. Inside the eval, the my_add_fn would do something like the below to increment it:

def my_add_fn():
   global count_iterations
   count_terations += 1
   return 'blah!'

Edit: I should have added this at first. Yes, I need to use eval. This eval is from user input originally but has been parsed into an Abstract Syntax Tree that rejects all but a few mathematical operations. Then, that AST is what is being eval'd with some custom function definitions defined.

Sounds like I can't do it this way though.

You should not be using eval to start with. But since you don't show the code you are running in there, there is no way to suggest alternatives.

The fact is Python's eval only executes expressions - and assignments in Python are statements - you can't, ordinarily, have an assignment inside an eval.

The way to fix that is just to use exec instead of eval. By default, exec will use the global variables of the module where it is run, so, any assignment in the executed code will affect the global namespace.

If you pass a dictionary as a second argument to exec it will use that dictionary as its globals dictionary - and then you can have it modify the values inside that dict with assignments.

That are a general statement about eval and exec - your specific problem is that you are (likely) trying to call a function inside eval - add() - which is exposed to it in the globals_defined dictionary itself. The problem is your add function is defined in your module - outside the eval (or exec) scope. The Global variables for the my_add_fn function are not the globals passed to eval, rather the module globals themselves. So the variable count_iterations it accesses is on the module scope, not inside the globals_defined dictionary.

Of course there are ways to fix that. The best is: not to use eval (or exec). Your print idiom on the last line suggests you are new to Python - otherwise you would know about the powerfull ways of string formatting in existence. That said: you can call functions stored in a dictionary just as you can access any element on a dictionary - that is: globals_defined["add"] () will all your my_add_fn function - this is a hint of how you can get rid of the need to use "eval".

And finally, back to your problem as is - one way to solve it, is to have your function not rely on its global namespace, and rather receive and return parameters - and you then make assignments of the values returned by the function outside it - on your exec string scope - thus:

def my_add_fn(count):
   return 'blah!', count+1

global_dict = {"add": my_add_fn, "count_iterations": 0} 

exec("result, count_iterations = add()", global_dict)

print "Count = {}".format(global_dict["count_iterations"])

Why try to use a global namespace? Why not construct a specific local namespace to pass the functions to the eval ? The problem with passing globals() as the global namespace to eval or exec is that you are giving away control of your namespace to the code you exit, so (eg) if it binds to a name you are already using your value will be overwritten.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM