Python exec不访问变量,exec可选

2024-04-25 01:20:29 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在写一个命令行计算器。我把所有的数学都记下来了,但我想添加自己的函数,比如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

输出的前三行告诉我脚本的每个部分都会执行,但是finalAnswer没有初始化。我错过了使用exec()的一些细微差别。你知道吗

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


Tags: 函数indexreturnfooparamdef数学result
2条回答

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

不管怎样,这里有一个解决办法。我们将批准的函数存储在一个dict中,由函数名键入。我们假设包含函数参数列表的字符串是一个元组,并获取literal_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_eval对于它在字符串中接受的内容非常严格。从文档中:

Safely evaluate an expression node or a string containing a Python literal or container display. The string or node provided may only consist of the following Python literal structures: strings, bytes, numbers, tuples, lists, dicts, sets, booleans, and None.

容器显示是列表、元组、集合或dict文本,例如(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'")
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)

相关问题 更多 >