在 eval 中修改 Python 全局变量

3

我有这样的代码:

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

我想在 eval 内部设置一个全局变量,并且之后能够访问它。就像这样:
```javascript eval("var globalVar = 'hello';"); console.log(globalVar); ```
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']) 

理想情况下,这将打印出 count_iterations 的修改值。在 eval 中,my_add_fn 将执行类似以下的操作以递增它:
def my_add_fn():
   global count_iterations
   count_terations += 1
   return 'blah!'

编辑:我应该先补充一下。是的,我需要使用eval。这个eval最初来自用户输入,但已被解析为抽象语法树,拒绝除少数数学运算外的所有操作。然后,这个AST被使用一些自定义函数定义进行eval。听起来似乎不能以这种方式来实现。

5
你为什么有这样的代码?你试图解决什么问题? - jonrsharpe
2个回答

3
你不应该使用eval。但由于你没有展示你运行的代码,所以没有办法建议替代方案。
事实上,Python的eval只执行表达式——而Python中的赋值是语句——通常情况下,你不能在eval中有一个赋值。
解决这个问题的方法就是使用exec而不是eval。默认情况下,exec会使用它所在模块的全局变量,因此,执行的代码中的任何赋值都将影响全局命名空间。
如果你将字典作为exec的第二个参数传递进去,它将使用该字典作为其全局字典,然后你可以使用赋值语句修改其中的键值。
这是关于eval和exec的一般性陈述——你的特定问题是,你(很可能)正试图在eval中调用一个函数——add()——它在globals_defined字典中本身就暴露出来了。问题在于,你的add函数定义在模块之外——在eval(或exec)范围之外。my_add_fn函数的全局变量不是传递给eval的globals,而是模块globals本身。因此,它访问的count_iterations变量在模块范围内,而不是在globals_defined字典内。
当然,有几种方法可以解决这个问题。最好的方法是:不要使用eval(或exec)。你在最后一行的打印习惯表明你是新手——否则,你会知道存在强大的字符串格式化方法。也就是说:你可以调用存储在字典中的函数,就像你可以访问字典上的任何元素一样——也就是说:globals_defined["add"] ()会调用你的my_add_fn函数——这是一个提示,你可以摆脱使用"eval"的需要。
最后,回到你的问题本身——解决它的一种方法是,让你的函数不依赖于它的全局命名空间,而是接收和返回参数——然后你在exec字符串范围之外使用函数返回的值进行赋值。
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"])

我也不能这样做,因为会运行多个 eval。 - user1387717
那么?将多个eval更改为多个exec。 - Kevin
相反,改变代码以动态调用所需的函数,而不使用 eval。 :-) - jsbueno

0
为什么要使用全局命名空间?为什么不构建一个特定的本地命名空间来传递函数给eval呢?将globals()作为全局命名空间传递给evalexec的问题在于,你将控制权交给了退出代码,所以如果它绑定到你已经使用的名称上,你的值将被覆盖。

他/她实际上并没有这样做 - 他/她正在传递一个自定义字典。问题在于调用了一个试图更改全局变量的函数 - 但该函数的全局变量不是传递给eval的全局变量。 - jsbueno

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接