这是一个非常有限的示例,它展示了您如何做到这一点——当然,这里有一些限制——主要是,这仅在用户只输入一个函数时才有效。如果他们写的字符串看起来更像:
a='garbage'
def foo():pass
甚至:
def bar():
return foobar()
def foobar():
return "foobar is a cool word, don't you think?"
那你就不走运了。(换句话说,这假设用户只向 run_user 函数的命名空间添加一件事)。当然,您可以检查它并引发异常,或者如果结果是用户添加了太多...您也可以返回该函数并按照 gauden 的建议使用它。
def run_user(S):
#S is the user's function as a string.
lvars=None #make sure the name is in locals()
lvars=set(locals())
exec(S) #exec isn't usually a good idea -- but I guess you're a very trusting person.
usr_namespace=list(set(locals())-lvars)
usr_func_name=usr_namespace[0]
if(len(usr_namespace)>1):
raise ValueError("User input too much into the namespace!")
usr_func=locals()[usr_func_name]
usr_func() #comment this out if you don't want to run the function immediately
return usr_func
usr_string="""
def foo():
a="Blah"
print "Hello World! "+a
"""
func_handle=run_user(usr_string) #prints "Hello World! Blah"
#and to demonstrate that we can pass a handle to the function around:...
func_handle() #prints "Hello World! Blah" again.
请注意,您可以使用 python 3exec
或 python 2更安全地执行此操作,您可以通过将字典作为全局字典execfile
传递来限制用户函数的命名空间{'__builtins__':None}
#python3.x
allowed=vars(__builtins__).copy()
allowed['__import__']=None
exec("import os",{'__builtins__':None},allowed) #raises ImportError
exec("print(abs(-4))",{'__builtins__':None},allowed) #prints 4 as you'd expect.
execfile
如果您将字符串写入临时文件,我希望在 python2.x 下可以使用相同的东西......
编辑(解决下面的评论)
您提供的示例eval
可以更简单地完成:
a=5
b=eval('a+5') #b == 10
但是,这不是您要求的。您要求的是用户可以编写一个函数,例如:
def f(a):
return a+5
前一种情况可以,但用户需要知道变量名是'a'。
a=5
b=eval('x+5') #won't work -- x isn't defined
他们还需要知道如何添加向量——(如果您使用的是微不足道的 numpy 数组,但我想我会提到它以防万一)。而且,如果没有大量的工作和令人头疼的问题,他们就无法制作复杂的表达式(使用多个条件、循环等的长表达式)。
后一种情况要好一些(在我看来),因为它更通用。您可以使用我描述的方法获取该函数(删除其中我实际运行该函数的部分)并且用户可以使用他们想要的任何变量名称 - 然后您只需使用他们的函数。他们还可以执行循环之类的操作,并使用比在一行中使用eval
. 您为此付出的唯一一件事是用户需要编写def func(...):
,return some_value
最后,如果他们知道 python 应该是完全直观的。
ss="""
def foo(x):
return 5+x
"""
a=5
func=run_user(ss)
result=func(a) #result = 10
这还有一个好处,就是每次调用函数时都不需要重新解析字符串。拥有func
后,您可以随心所欲地使用它。另请注意,使用我的解决方案,您甚至不需要知道用户定义的函数的名称。一旦有了函数对象,名称就无关紧要了。