我目前正在尝试使用tkinter编写Python(3.4.4)GUI,这应该允许将任意函数适合某些数据点。为了简单起见,我想创建一些输入函数并对其进行评估。之后,我想使用curve_fit
中的scipy
来绘制并拟合它。
为了做到这一点,我想从user-input-string创建一个动态(拟合)函数。我发现并阅读了exec
,但人们说(1)使用起来不安全,(2)总有更好的选择(例如here和许多其他地方)。所以,我想知道在这种情况下会有什么替代方案?
下面是一些带有两个嵌套函数的示例代码,但它不是动态的:
def buttonfit_press():
def f(x):
return x+1
return f
print(buttonfit_press()(4))
这里有一些代码在我开始使用xval之前产生NameError: name 'f' is not defined
:
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
的替代方法(10303248)也不成功......
所以,我的问题是:我可以在这种情况下使用一个很好的替代方案吗?如果没有,我如何使用exec
运行代码?
我希望这是可以理解的,而不是太模糊。提前感谢您的想法和意见。
@GáborErdő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
@ŁukaszRogalski:
打印execstr
对我来说似乎很好:
def f(x):
return x+1
由于我的编辑器不太可能出现缩进错误,但我仔细检查了 - 没关系。
介绍my_locals
,在exec
中调用它并在之后打印显示:
{'f': <function f at 0x000000000348D8C8>}
但是,我仍然得到NameError: name 'f' is not defined
。
@ user3691475:
您的示例与我的第一个示例非常相似。但是在我的理解中这不是“动态的”,即在代码运行时不能改变函数的输出。
@Dunes:
我认为这是正确的方向,谢谢。但是,我还不明白如何在下一步中评估和使用此功能?我的意思是:为了能够适应它,我必须提取拟合变量(即a
中的f(x)=a*x+b
)或评估各种x值的函数(即print(f(3.14))
)。
答案 0 :(得分:1)
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
答案 1 :(得分:0)
我不确定你到底想要做什么,即允许哪些功能,允许哪些操作等等。
以下是带有一个动态参数的函数发生器的示例:
>>> def generator(n):
def f(x):
return x+n
return f
>>> plus_one=generator(1)
>>> print(plus_one(4))
5