Как я могу позволить пользователям безопасно выполнять математические выражения? Нужно ли писать полный анализатор?
Есть ли что-то вроде ast.literal_eval () , но для выражений?
На странице Примеры Pyparsing перечислены несколько парсеров выражений:
http://pyparsing.wikispaces.com/file/view/fourFn.py - A реализация парсера/оценщика обычной арифметической инфиксной нотации с использованием pyparsing (несмотря на название, на самом деле это 5-функция арифметики плюс несколько триггерных функций)
http://pyparsing.wikispaces.com/file/view/simpleBool.py — парсер/оценщик логической инфиксной нотации, использующий вспомогательный метод pyparsing operatorPrecedence
, который упрощает определение нотации инфиксных операторов
http://pyparsing.wikispaces.com/file/view/simpleArith. py http://pyparsing.wikispaces.com/file/view/eval_arith.py -
Пара примеров преобразования FourFn.py с использованием operatorPrecedence
. Первый просто анализирует и возвращает дерево разбора, второй добавляет логику оценки.
Да. Даже если бы существовал эквивалент ast.literal_eval()
для выражений, выражение Python может быть множеством других вещей, кроме чисто математического выражения, например вызовом произвольной функции.
Меня не удивит, если в каком-нибудь модуле с открытым исходным кодом уже есть хороший анализатор/оценщик математических выражений, но если нет, то довольно легко написать свой собственный.
Несколько недель назад я сделал нечто подобное, но для логических выражений (или, и, не, сравнения, круглые скобки и т.д.). Я сделал это, используя парсер Ply. Я создал простой лексер и парсер. Парсер сгенерировал дерево AST, которое позже использовалось для выполнения вычислений. Выполнение этого таким образом позволит вам полностью контролировать то, что вводит пользователь, потому что будут анализироваться только выражения, совместимые с грамматикой.
математические функции будут состоять из цифр и знаков препинания, возможно, 'E' или 'e' если вы разрешите научную запись для рациональных чисел, и единственное (другое) законное использование альфа-символов будет, если вы разрешите/предоставите определенные математические функции (например, stddev). Таким образом, должно быть тривиально пробежаться по строке для альфа-символов и проверить, не является ли следующий бит подозрительным, а затем просто проверить строку в блоке try/except.
Что касается комментариев, которые получил этот ответ... Я согласен, что такой подход — игра с огнем. Тем не менее, это не означает, что это нельзя сделать безопасно. Я новичок в python (< 2 месяцев), поэтому, возможно, не знаю обходных путей, к которым это уязвимо (и, конечно, новая версия Python всегда может сделать код небезопасным в будущем), но - для чего это стоит ( в основном мое собственное развлечение) - вот мой щелчок по этому поводу:
def evalMaths(s):
i = 0
while i < len(s):
while s[i].isalpha() and i < len(s):
idn += s[i]
i += 1
if (idn and idn != 'e' and idn != 'abs' and idn != 'round'):
raise Exception("you naughty boy: don't " + repr(idn))
else:
i += 1
return eval(s)
Мне было бы очень интересно услышать, можно ли/как это обойти... (^_^) Кстати, я знаю, что вы можете вызывать такие функции, как abs2783 или _983 - если бы они существовали, но их не будет. Я имею в виду что-то практичное.
На самом деле, если кто-то может это сделать, я создам вопрос с вознаграждением в 200 и приму их ответ.
Какие выражения вам нужны? Присвоение переменной? Оценка функции?
SymPy
стремится стать полноценной Python CAS.