Использование numba.jit в строке ввода пользователя - PullRequest
0 голосов
/ 01 июня 2018

Я пытаюсь написать программу, которая принимает функцию пользовательского ввода в виде строки и выполняет ряд вычислений, используя эту функцию.Эти вычисления выполняются с использованием numba.jit, и код работает, если я жестко закодирую свою функцию, но у меня возникают проблемы с выяснением, как разобрать строку таким образом, чтобы я мог превратить ее в функцию с сопряжением с nopython = True.

Например, мой код работает с функцией

@jit(nopython=True)
def f(x):
    return x*x

, но я хочу вместо этого взять строку ввода пользователя 'x * x' и создать ту же функцию.Я пытался использовать SymPy, но я не мог заставить его играть хорошо с Jit.Есть идеи?

1 Ответ

0 голосов
/ 01 июня 2018

Предполагая, что вы на самом деле хотите принять произвольный код Python в качестве пользовательского ввода и запустить его, чего вы обычно действительно не хотите делать, но давайте предположим, что у вас есть веская причина ...

Ново-первых: вы упомянули попытки сделать это с 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}'))

Если выВы думаете: «Но подождите, eval опасно» - ну, да, 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)
...