在eval中修改Python全局变量

时间:2016-06-21 13:38:41

标签: python eval

我有这样的代码:

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中设置一个全局变量,然后能够访问它。所以喜欢:

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是定义了一些自定义函数定义的评估。

听起来我不能这样做。

2 个答案:

答案 0 :(得分:3)

你不应该使用eval开始。 但是,由于您没有显示您在那里运行的代码,因此无法建议替代方案。

事实是Python的eval只执行表达式 - 而Python中的赋值是statements - 你通常不能在eval中有一个赋值。

解决这个问题的方法就是使用exec而不是eval。默认情况下,exec将使用运行它的模块的全局变量,因此,执行代码中的任何赋值都将影响全局命名空间。

如果您将字典作为第二个参数传递给exec,它将使用该字典作为其全局字典 - 然后您可以通过分配修改该字典中的值。

这是关于eval和exec的一般声明 - 你的具体问题是你(可能)试图在eval中调用一个函数 - add() - 它在globals_defined字典中公开了本身。问题是你的模块中定义了你的add函数 - 在eval(或exec)范围之外。 my_add_fn函数的全局变量不是传递给eval的globals,而是模块全局变量本身。因此,它访问的变量count_iterations位于模块范围内,而不是globals_defined字典内。

当然有办法解决这个问题。最好的是:不使用eval(或exec)。 你在最后一行的打印习语暗示你是Python的新手 - 否则你就会知道存在的字符串格式化的强大方法。 这就是说:您可以调用存储在字典中的函数,就像您可以访问字典中的任何元素一样 - 即: 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"])

答案 1 :(得分:0)

为什么要尝试使用全局命名空间?为什么不构造一个特定的本地命名空间来将函数传递给eval?将globals()作为全局命名空间传递给evalexec的问题在于,您将命名空间的控制权交给您退出的代码,因此(例如)如果它绑定到你已经在使用你的价值的名字将被覆盖。