Python exec的替代方案

Python exec的替代方案,python,Python,我目前正在尝试用tkinter编写Python(3.4.4)GUI,它应该允许将任意函数适配到某些数据点。为了简单起见,我想创建一些输入函数并对其进行评估。稍后,我将使用scipy中的curve\u-fit对其进行绘制和拟合 为此,我想从用户输入字符串创建一个动态(拟合)函数。我发现并阅读了有关exec,但人们说(1)使用它不安全,(2)总是有更好的替代品(例如,在许多其他地方)。所以,我想知道在这种情况下的替代方案是什么 下面是一些示例代码,其中包含两个嵌套函数,它们可以工作,但不是动态的:

我目前正在尝试用tkinter编写Python(3.4.4)GUI,它应该允许将任意函数适配到某些数据点。为了简单起见,我想创建一些输入函数并对其进行评估。稍后,我将使用
scipy
中的
curve\u-fit
对其进行绘制和拟合

为此,我想从用户输入字符串创建一个动态(拟合)函数。我发现并阅读了有关
exec
,但人们说(1)使用它不安全,(2)总是有更好的替代品(例如,在许多其他地方)。所以,我想知道在这种情况下的替代方案是什么

下面是一些示例代码,其中包含两个嵌套函数,它们可以工作,但不是动态的:

def buttonfit_press():
    def f(x):
         return x+1
    return f 

print(buttonfit_press()(4))
下面是一些导致
name错误的代码:在我开始使用xval之前,还没有定义名称“f”:

def buttonfit_press2(xval):
    actfitfunc = "f(x)=x+1"
    execstr = "def {}:\n    return {}\n".format(actfitfunc.split("=")[0], actfitfunc.split("=")[1])
    exec(execstr)
    return f

print(buttonfit_press2(4))
这里讨论的另一种使用
types.FunctionType
的方法()也没有成功

所以,我的问题是:对于这个场景,有没有一个好的替代方案?或者,如果没有,如何使
exec
运行代码

我希望这是可以理解的,不要太含糊。提前感谢您的想法和意见


@Gábor Erdős:

要么我不明白,要么我不同意。如果我在主循环中编码相同的代码段,它会识别
f
,我可以从
execstr
执行代码段:

actfitfunc = "f(x)=x+1"
execstr = "def {}:\n    return {}\n".format(actfitfunc.split("=")[0], actfitfunc.split("=")[1])
exec(execstr)
print(f(4))
>>> 5

@Łukasz Rogalski:

打印
execstr
对我来说很好:

def f(x):
    return x+1
缩进错误不太可能是由于我的编辑器造成的,但我仔细检查了一下-没问题。 介绍
my_locals
,在
exec
中调用它,然后在中打印显示:

{'f': <function f at 0x000000000348D8C8>}
{'f':}
但是,我仍然得到了
NameError:没有定义名称“f”


@用户3691475:

你的例子与我的第一个例子非常相似。但在我的理解中,这不是“动态的”,也就是说,在代码运行时不能更改函数的输出


@沙丘:


我认为这是正确的方向,谢谢。然而,我还不明白下一步如何评估和使用这个函数?我的意思是:为了能够拟合它,我必须提取拟合变量(即
f(x)=a*x+b
)或在不同的x值(即
打印(f(3.14))
)下计算函数。

我不确定你到底想做什么,即允许什么函数,允许什么操作,等等

以下是具有一个动态参数的函数生成器示例:

>>> def generator(n):
        def f(x):
            return x+n
        return f
>>> plus_one=generator(1)
>>> print(plus_one(4))
5

exec/eval的问题是,它们可以执行任意代码。因此,要使用
exec
eval
,您需要仔细解析代码片段,以确保其不包含恶意代码(这是一项非常困难的任务),或者确保代码的源代码是可信的。如果你正在制作一个供个人使用的小程序,那没关系。一个负责敏感数据或金钱的大程序,绝对不是。您的用例似乎被视为具有可信的源

如果您只想在运行时创建任意函数,那么只需结合使用lambda表达式和
eval
。例如

func_str = "lambda x: x + 1" # equates to f(x)=x+1
func = eval(func_str)
assert func(4) == 5 
您的尝试不起作用的原因是,
locals()
在函数的上下文中创建了本地命名空间的副本。对结果字典的突变不会影响当前本地命名空间。您需要执行以下操作:

def g():
    src = """
def f(x):
    return x + 1
    """
    exec_namespace = {} # exec will place the function f in this dictionary
    exec(src, exec_namespace)
    return exec_namespace['f'] # retrieve f

@从技术上讲,GáborErdő是这样的,因为exec会隐式地传递对
globals()
locals()
的引用以及输入参数,并且它们会发生变异。@nostradamus您的代码应该可以正常工作。你的缩进正确吗?你能运行:
my_locals={};exec(execstr,globals(),my_locals)
并在之后显示存储在
my_locals中的内容?通过打印
execstr
进行健全性检查也不会造成伤害。