如何从 Python exec()/eval() 调用中获取结果?
How to get results out of a Python exec()/eval() call?
我想在 Python 中编写一个工具来准备模拟研究,方法是为每个模拟 运行 创建一个文件夹和一个包含一些 运行 特定参数的配置文件。
study/
study.conf
run1
run.conf
run2
run.conf
该工具应从文件中读取整个研究配置,包括 (1) 静态参数(键值对),(2) 迭代参数列表,以及 (3) 一些小代码片段以从中计算更多参数以前的。后者 运行 具体取决于所用迭代参数的排列。
在从模板编写 run.conf 文件之前,我需要 运行 一些这样的代码来确定 运行[的代码片段中的特定键值对=16=]
code = compile(code_str, 'foo.py', 'exec')
rv=eval(code, context, { })
但是,正如 Python 文档所确认的那样,这只会导致 None
作为 return 值。
示例中的代码串和上下文字典在别处填写。对于这个讨论,这个片段应该做到:
code_str="""import math
math.sqrt(width**2 + height**2)
"""
context = {
'width' : 30,
'height' : 10
}
我以前在 Perl 和 Java+JavaScript 中做过这个。在那里,您只需将代码片段提供给某个评估函数或脚本引擎,然后从上次执行的语句中获取 return 一个值(对象)——这不是什么大问题。
现在,在 Python 中,我纠结于以下事实:eval() 过于狭窄,只允许一个语句,而 exec() 一般不会 return 值。我需要导入模块,有时会做一些稍微复杂的计算,例如 5 行代码。
难道就没有更好的解决方案吗?
在我的研究过程中,我发现了一些关于 Pyhton eval() and exec() and also some tricky solutions to circumvent the issue by going via the stdout 和从那里解析 return 值的非常好的讨论。后者会做,但不是很好而且已经5岁了。
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
字典中以 key:value 对结束。所以字典就是你在 JavaScript.
中提到的 "object"
编辑 1:
如果您只需要 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
因为 exec()
/ eval()
Python 中的整个东西有点奇怪......我选择根据评论中的建议重新设计整个东西我的问题(感谢@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()
我也遇到过类似的需求,最后通过玩ast找到了一个方法:
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))
如果最后一个子句是表达式,它会评估而不是执行它,或者全部执行并且 return None.
输出:
got locals: {'tf': <function tf at 0x10103a268>, 'r': 9}
got return: {'vvv': 25}
我想在 Python 中编写一个工具来准备模拟研究,方法是为每个模拟 运行 创建一个文件夹和一个包含一些 运行 特定参数的配置文件。
study/
study.conf
run1
run.conf
run2
run.conf
该工具应从文件中读取整个研究配置,包括 (1) 静态参数(键值对),(2) 迭代参数列表,以及 (3) 一些小代码片段以从中计算更多参数以前的。后者 运行 具体取决于所用迭代参数的排列。
在从模板编写 run.conf 文件之前,我需要 运行 一些这样的代码来确定 运行[的代码片段中的特定键值对=16=]
code = compile(code_str, 'foo.py', 'exec')
rv=eval(code, context, { })
但是,正如 Python 文档所确认的那样,这只会导致 None
作为 return 值。
示例中的代码串和上下文字典在别处填写。对于这个讨论,这个片段应该做到:
code_str="""import math
math.sqrt(width**2 + height**2)
"""
context = {
'width' : 30,
'height' : 10
}
我以前在 Perl 和 Java+JavaScript 中做过这个。在那里,您只需将代码片段提供给某个评估函数或脚本引擎,然后从上次执行的语句中获取 return 一个值(对象)——这不是什么大问题。
现在,在 Python 中,我纠结于以下事实:eval() 过于狭窄,只允许一个语句,而 exec() 一般不会 return 值。我需要导入模块,有时会做一些稍微复杂的计算,例如 5 行代码。
难道就没有更好的解决方案吗?
在我的研究过程中,我发现了一些关于 Pyhton eval() and exec() and also some tricky solutions to circumvent the issue by going via the stdout 和从那里解析 return 值的非常好的讨论。后者会做,但不是很好而且已经5岁了。
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
字典中以 key:value 对结束。所以字典就是你在 JavaScript.
编辑 1:
如果您只需要 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
因为 exec()
/ eval()
Python 中的整个东西有点奇怪......我选择根据评论中的建议重新设计整个东西我的问题(感谢@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()
我也遇到过类似的需求,最后通过玩ast找到了一个方法:
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))
如果最后一个子句是表达式,它会评估而不是执行它,或者全部执行并且 return None.
输出:
got locals: {'tf': <function tf at 0x10103a268>, 'r': 9}
got return: {'vvv': 25}