我正在尝试编写一个程序,它将用户输入函数作为字符串,并使用该函数进行大量计算。这些计算是使用numba.jit完成的,如果我对我的函数进行硬编码,代码就可以工作,但是我无法弄清楚如何解析字符串,以便我可以将它转换为带有nopython = True的jitted函数。
例如,我的代码使用函数
运行@jit(nopython=True)
def f(x):
return x*x
但我希望将用户输入字符串'x * x'设置为创建相同的函数。我已经尝试过使用SymPy,但是我无法让它与jit很好地配合使用。有什么想法吗?
答案 0 :(得分:3)
假设你真的想把任意Python代码作为用户输入并运行它 - 你通常真的,真的不想做,但让我们假设你有充分的理由... < / p>
但首先:你提到过试图用SymPy做这件事。如果您实际上尝试使用SymPy表达式创建函数,例如,使用sympify
或lambdify
- 这应该有效,如果它不起作用,则需要显示如果您需要帮助调试它,请使用您的代码。
但是,让我们停止拖延并了解你如何做你所要求的,尽管它很有可能它实际上并不是你想要的。
请记住,装饰器只是函数,它接受一个函数并返回另一个函数,你可以正常调用它们。因此,您所要做的就是将任意Python代码转换为函数,然后将其传递给装饰器。
如果任意Python代码只是一个表达式,你可以将它包装在lambda
表达式中,eval
结果,并且你有一个应用该表达式的函数:
lambdastr = f'lambda x: {user_string}'
lambdafunc = eval(lambdastr)
numbafunc = numba.jit(nopython=True)(lambdafunc)
或者,如果您愿意:
numbafunc = numba.jit(nopython=True)(eval(f'lambda x: {user_string}'))
如果您正在思考&#34;但是等等,eval
很危险&#34; -well,是的,eval
是危险的,因为它会将任意用户字符串作为代码进行评估,这是正是你想要做的。没有非危险的方法可以做到这一点。
因此,如果您的用户将字符串x * x
传给您,您现在已经获得了一个对其输入进行平方的功能,如果用户将字符串__import__('os').system('rm -rf /')
传递给您,您就可以了。我现在有一个功能,试图擦除你的整个硬盘。
如果你想发表一个声明,你可以通过将其包裹在def
并调用exec
来有效地做同样的事情:
defstr = f'def __(x): {user_string}'
deffunc = exec(defstr)
numbafunc = numba.jit(nopython=True)(deffunc)
如果任意Python代码可以是一个语句块,那么由于你需要处理缩进这一点会稍微复杂一些,但这并不太难:
user_lines = '\n'.join(' '+line for line in user_string.splitlines())
defstr = f'def __(x):\n{user_lines}'
deffunc = exec(defstr)
numbafunc = numba.jit(nopython=True)(deffunc)