如何从Python exec()/ eval()调用中获得结果?

时间:2016-05-15 10:17:20

标签: python exec eval

我想用Python编写一个工具,通过为每个模拟运行创建一个包含一些特定于运行的参数的文件夹和配置文件来准备模拟研究。

study/
  study.conf
  run1
    run.conf
  run2
    run.conf

该工具应从文件中读取整个研究配置,包括(1)静态参数(键值对),(2)迭代参数列表,以及(3)一些小代码片段,用于计算前一个参数那些。后者根据所使用的迭代参数的排列而具体运行。

在从模板编写run.conf文件之前,我需要运行这样的代码来确定该运行的代码片段中的特定键值对

code = compile(code_str, 'foo.py', 'exec')
rv=eval(code, context, { })

但是,正如Python文档所证实的那样,这只会导致None作为返回值。

示例中的代码字符串和上下文字典在其他地方填充。对于此讨论,此代码段应该执行此操作:

code_str="""import math
math.sqrt(width**2 + height**2)
"""

context = {
    'width' : 30,
    'height' : 10
}

我之前在Perl和Java + JavaScript中做过这个。在那里,您只需将代码片段提供给某个评估函数或脚本引擎,并从上一个执行的语句返回一个值(对象) - 这不是一个大问题。

现在,在Python中,我很难理解eval()太窄而只允许一个语句而exec()一般不会返回值。我需要导入模块,有时会做一些稍微复杂的计算,例如5行代码。

目前我没有看到更好的解决方案吗?

在我的研究过程中,我发现了一些关于Pyhton eval() and exec()的非常好的讨论以及一些棘手的解决方案,以便通过going via the stdout来解决问题并从那里解析返回值。后者会这样做,但不是很好,已经5岁了。

3 个答案:

答案 0 :(得分:0)

exec函数将修改传递给它的全局参数(dict)。所以你可以使用下面的代码

code_str="""import math
Result1 = math.sqrt(width**2 + height**2)
"""
context = {
    'width' : 30,
    'height' : 10
}

exec(code_str, context)

print (context['Result1']) # 31.6

创建的每个变量code_str都会以context字典中的键:值对结束。所以dict是"对象"就像你在JavaScript中提到的那样。

EDIT1:

如果您只需要code_str中最后一行的结果并尝试阻止Result1=...之类的内容,请尝试以下代码

code_str="""import math
math.sqrt(width**2 + height**2)
"""

context = { 'width' : 30, 'height' : 10 }

lines = [l for l in code_str.split('\n') if l.strip()]
lines[-1] = '__myresult__='+lines[-1] 

exec('\n'.join(lines), context)
print (context['__myresult__'])

这种方法不如前一种方法强大,但对大多数情况应该有效。如果您需要以复杂的方式操作代码,请查看 Abstract Syntax Trees

答案 1 :(得分:0)

因为Python中的整个exec() / eval()事情有点奇怪......我选择根据我的问题的评论中的提议重新设计整个事情(感谢@ jonrsharpe)。

现在,整个学习规范是用户可以编辑的.py模块。从那里,配置设置直接写入整个包的中心对象。在工具运行中,使用下面的代码导入配置模块

import imp

# import the configuration as a module
(path, name) = os.path.split(filename)
(name, _) = os.path.splitext(name)
(file, filename, data) = imp.find_module(name, [path])

try:
    module = imp.load_module(name, file, filename, data)
except ImportError as e:
    print(e)
    sys.exit(1)
finally:
    file.close()

答案 2 :(得分:0)

我遇到了类似的需求,最后通过玩游戏找到了一种方法:

import ast
code = """
def tf(n):
  return n*n
r=tf(3)
{"vvv": tf(5)}
"""
ast_ = ast.parse(code, '<code>', 'exec')
final_expr = None
for field_ in ast.iter_fields(ast_):
    if 'body' != field_[0]: continue
    if len(field_[1]) > 0 and isinstance(field_[1][-1], ast.Expr):
        final_expr = ast.Expression()
        final_expr.body = field_[1].pop().value
ld = {}
rv = None
exec(compile(ast_, '<code>', 'exec'), None, ld)
if final_expr:
    rv = eval(compile(final_expr, '<code>', 'eval'), None, ld)
print('got locals: {}'.format(ld))
print('got return: {}'.format(rv))

如果它是一个表达式,或者全部执行并返回None,那么它将eval而不是执行最后一个子句。

输出:

got locals: {'tf': <function tf at 0x10103a268>, 'r': 9}
got return: {'vvv': 25}