Python exec未访问变量,exec替代方案

Python exec未访问变量,exec替代方案,python,exec,eval,Python,Exec,Eval,我正在写一个命令行计算器。我把所有的数学都记下来了,但是我想添加我自己的函数,比如solve(“x^3=8”,x)。我的数学很好,但我使用exec()的方式阻止它将结果写入局部变量finalAnswer 我加入了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 _

我正在写一个命令行计算器。我把所有的数学都记下来了,但是我想添加我自己的函数,比如solve(“x^3=8”,x)。我的数学很好,但我使用exec()的方式阻止它将结果写入局部变量finalAnswer

我加入了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
addAB(15,8)
5.
23
回溯(最近一次呼叫最后一次):
文件“C:/Users/mikeo/OneDrive/Documents/function filter.py”,第45行,在
打印(finalAnswer)
NameError:未定义名称“finalAnswer”
输出的前三行告诉我脚本的每一部分都会执行,但是finalAnswer没有初始化。我是否错过了使用exec()的一些细微差别


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

此处可能不需要
exec
eval
。如果您的表达式都很简单,如示例代码中所示,那么我们可以使用它(顾名思义)来计算包含有效Python文本的字符串。这使得它比普通的
eval
exec
安全得多。当然,您的代码只尝试执行已批准的函数,因此应该是安全的,但仍然

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)
无论如何,这里有一个解决方案。我们将批准的函数存储在dict中,并由函数名键入。我们假设包含函数参数列表的字符串是一个元组,并获取
literal\u eval
来为我们构建该元组,这样我们就可以使用
*
序列解包将参数传递给函数

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\u eval
对它在字符串中接受的内容非常严格。从文档中:

安全地计算表达式节点或包含Python的字符串 文字或容器显示。提供的字符串或节点只能是 由以下Python文本结构组成:字符串、字节、, 数字、元组、列表、dicts、set、boolean和
None

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

"(15, f(8))"

它将引发
ValueError:节点或字符串格式错误

这有一个小小的例外
ast.literal\u eval
将接受仅使用
+
-
的算术表达式。这是因为它需要能够计算复数文本,而这些文本包含
+
-
。实现者决定,简单的处理方法是允许
.literal\u eval
计算包含
+
-
的算术表达式;这样做不会产生安全风险。但这只适用于算术表达式,不能使用
+
进行字符串连接,因此

a = ast.literal_eval("'ab' + 'cd'")
将引发
ValueError
;OTOH,它确实接受相邻字符串文本的通常自动连接,因此这是可以的:

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

看起来你真正想要的是用它来计算你的表达式,并将结果赋给一个变量,但是一般认为编写这样的代码不是一个好主意。我喜欢加分@khelwood Mikeologist似乎明白这一点,这就是为什么他只尝试执行批准的功能。OTOH,除非对
param
字符串也进行了分析,否则足够坚定的攻击者仍然可以执行恶意代码。我知道我不应该使用exec(),但我需要能够定义一个可扩展的函数接口。除非我有误解,否则eval()不会与solve(“x^3+4=12”,x)等函数中的算法一起工作。但我在这里询问并删除exec()是我问题的一个额外规定,所以如果有更好的方法,我鼓励你将其作为答案发布。我明白了,所以它是有效的,我指的是finalAnswer,但因为我的第二个exec()调用是嵌套的,引用随该引用框一起丢弃?这是实现相同目标的更干净、更安全的方法。是否防止恶意使用func_参数?我这样问是因为你指出这是我的原始代码中潜在的安全缺陷。是的,这是安全的,因为
ast.literal\u eval
对接受的内容非常严格。我会把这些信息添加到我的答案中…太好了。在我提出这个问题之前的研究中,我无法找到eval()的替代方法来执行带参数的函数。这应该会有很大帮助。
a = ast.literal_eval("'ab' 'cd'")