在用户输入字符串上使用 numba.jit
Using numba.jit on a user input string
我正在尝试编写一个程序,将用户输入函数作为字符串接收,并使用该函数进行大量计算。这些计算是使用 numba.jit 完成的,如果我对我的函数进行硬编码,代码就可以工作,但是我无法弄清楚如何解析字符串,以便我可以使用 nopython= 将它变成一个 jitted 函数没错。
例如,我的代码运行函数
@jit(nopython=True)
def f(x):
return x*x
但我想取而代之的是获取用户输入字符串 'x*x' 并创建相同的函数。我试过使用 SymPy,但我无法让它与 jit 一起很好地发挥作用。有什么想法吗?
假设您确实想要将任意 Python 代码作为用户输入并 运行 它—您通常真的、真的不想这样做,但让我们假设您有一个很好的理由…
但首先:您提到尝试使用 SymPy 来做到这一点。如果您实际上是在尝试使用 SymPy 表达式创建函数,例如,通过使用 sympify
或 lambdify
——那应该可行,如果不行,您需要向我们展示您的代码,如果你需要帮助调试它。
但让我们停止拖延,看看如何做您要求的事情,即使这很可能不是您真正想要的。
记住装饰器只是函数,它接受一个函数和 return 另一个函数,您可以正常调用它们。因此,您所要做的就是将任意 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}'))
如果您认为 "But wait, eval
is dangerous" — 嗯,是的,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)
我正在尝试编写一个程序,将用户输入函数作为字符串接收,并使用该函数进行大量计算。这些计算是使用 numba.jit 完成的,如果我对我的函数进行硬编码,代码就可以工作,但是我无法弄清楚如何解析字符串,以便我可以使用 nopython= 将它变成一个 jitted 函数没错。
例如,我的代码运行函数
@jit(nopython=True)
def f(x):
return x*x
但我想取而代之的是获取用户输入字符串 'x*x' 并创建相同的函数。我试过使用 SymPy,但我无法让它与 jit 一起很好地发挥作用。有什么想法吗?
假设您确实想要将任意 Python 代码作为用户输入并 运行 它—您通常真的、真的不想这样做,但让我们假设您有一个很好的理由…
但首先:您提到尝试使用 SymPy 来做到这一点。如果您实际上是在尝试使用 SymPy 表达式创建函数,例如,通过使用 sympify
或 lambdify
——那应该可行,如果不行,您需要向我们展示您的代码,如果你需要帮助调试它。
但让我们停止拖延,看看如何做您要求的事情,即使这很可能不是您真正想要的。
记住装饰器只是函数,它接受一个函数和 return 另一个函数,您可以正常调用它们。因此,您所要做的就是将任意 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}'))
如果您认为 "But wait, eval
is dangerous" — 嗯,是的,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)