Python exec不访问变量,exec替代品

时间:2017-04-25 11:58:40

标签: python exec eval

我正在编写一个命令行计算器。我有所有数学,但我想添加自己的函数,如求解(" x ^ 3 = 8",x)。我的数学运算正常但我使用exec()的方式阻止它将结果写入局部变量fi​​nalAnswer。

我包含了foo()以显示我想要的内容

def foo():
    return 5
exec("a = foo()")
print(a)

result = "addAB(15,8)"

def addAB(A, B):
    print(A+B)
    return A+B

def __runFunc(fn, param):
    exec("finalAnswer = fn(" + param + ")")

__approvedFunctions = set(["addAB", "subtractAB"])

funcName = result[:result.index('(')]
if(funcName in __approvedFunctions):
    param = result[result.index('(')+1 : result.index(')')]
    if callable(globals()[funcName]):
        __runFunc(globals()[funcName], param)
print(finalAnswer)

执行时我的输出是:

addAB(15,8)
5
23
Traceback (most recent call last):
  File "C:/Users/mikeo/OneDrive/Documents/function filter.py", line 45, in 
<module>
    print(finalAnswer)
NameError: name 'finalAnswer' is not defined

输出的前三行告诉我脚本的每个部分都会执行,但finalAnswer不会被初始化。我是否错过了使用exec()的细微差别。

此外,如果您帮助我删除exec而不更改调用参数化函数的字符串输入的格式并在本地存储返回的值,则可获得奖励积分。

2 个答案:

答案 0 :(得分:1)

您可能不需要execeval。如果你的表达式都很简单,就像在你的示例代码中一样,那么我们可以使用ast.literal_eval,它(顾名思义)可以评估包含有效Python文字的字符串。这使得它比普通evalexec更安全。当然,您的代码只会尝试执行已批准的函数,因此 应该是安全的,但仍然......

无论如何,这是一个解决方案。我们将批准的函数存储在dict中,由函数名称键入。我们假装包含函数参数列表的字符串是一个元组,并获取literal_eval来为我们构建该元组,因此我们可以使用*序列解包将args传递给函数。

from ast import literal_eval

def addAB(A, B):
    print(A+B)
    return A+B

def subAB(A, B):
    print(A-B)
    return A-B

funcs = (addAB, subAB)
approved_functions = {func.__name__: func for func in funcs}

result = "addAB(15,8)"
i = result.index('(')
func_name, func_args = result[:i], literal_eval(result[i:])
print(func_name, func_args)

if func_name in approved_functions:
    func = approved_functions[func_name]
    final_answer = func(*func_args)
    print(final_answer)

<强>输出

addAB (15, 8)
23
23

这是安全的,因为ast.literal_eval 非常严格关于它在字符串中接受的内容。来自文档:

  

安全地评估表达式节点或包含Python的字符串   文字或容器显示。提供的字符串或节点可能只是   由以下Python文字结构组成:字符串,字节,   数字,元组,列表,dicts,集合,布尔值和None

容器显示是列表,元组,集合或字典文字,例如(2,3,4){'one':1, 'two': 2}。如果您尝试传递ast.literal_eval包含函数调用或甚至算术表达式的内容,例如

"(15, f(8))"

"(15, 2 * 4)"

它会引发ValueError: malformed node or string

这是一个小例外。 ast.literal_eval 接受仅使用+-的算术表达式。这是因为它需要能够评估复数文字,而这些文字包含+-。实施者决定,处理这种情况的简单方法是简单地允许.literal_eval评估包含+-的算术表达式;这样做不会产生安全风险。这只适用于算术表达式,你不能用+进行字符串连接,所以

a = ast.literal_eval("'ab' + 'cd'")

将提升ValueError; OTOH,它 接受相邻字符串文字的通常自动连接,所以这没关系:

a = ast.literal_eval("'ab' 'cd'")

答案 1 :(得分:0)

def foo():
    return 5
exec("a = foo()")
print(a)

result = "addAB(15,8)"

def addAB(A, B):
    print(A+B)
    return A+B

def __runFunc(fn, param):

    exec("globals()['finalAnswer']= fn(" + param + ")")

__approvedFunctions = set(["addAB", "subtractAB"])

funcName = result[:result.index('(')]
if(funcName in __approvedFunctions):
    param = result[result.index('(')+1 : result.index(')')]
    if callable(globals()[funcName]):
        __runFunc(globals()[funcName], param)
print(finalAnswer)