замените имена переменных фактическими значениями в выражении в AST python
У меня есть выражение, описанное в переменных формах, как это
's1*3 - (s2-s1)*1'
Я дал значения s1 и s2, которые могут изменяться в зависимости от необходимости
Я могу использовать модуль python ast для вычисления этого выражения путем замены соответствующих значений s1 и s2 (s1 = 20,s2=30)
import ast
import operator as op
operators = {ast.Add: op.add, ast.Sub: op.sub, ast.Mult: op.mul,
ast.Div: op.truediv, ast.Pow: op.pow, ast.BitXor: op.xor,
ast.USub: op.neg}
def eval_(node):
if isinstance(node, ast.Num): # <number>
return node.n
elif isinstance(node, ast.BinOp): # <left> <operator> <right>
return operators[type(node.op)](eval_(node.left), eval_(node.right))
elif isinstance(node, ast.UnaryOp): # <operator> <operand> e.g., -1
return operators[type(node.op)](eval_(node.operand))
else:
raise TypeError(node)
>>> str1 = '20*3 - (30-20)*1'
>>> node = ast.parse(str1, mode='eval')
>>> eval_(node.body)
50
Как я должен оценивать это выражение без необходимости заменять переменные их фактическими значениями.
Спасибо
3 ответа:
Вы можете использовать функцию
eval
. Но вы должны быть осторожны с использованиемeval
, потому что он выполняет любую строку, может быть очень опасно, если вы принимаете строки для оценки из ненадежных входных данных. например, предположим, что вычисляемая строка"os.system('rm -rf /')"
? Он действительно начнет удалять все файлы на вашем компьютере.>>> eval('20*3 - (30-20)*1') 50
В качестве лучшего решения вы можете проанализировать свое уравнение с помощью внутреннего компилятора python :
>>> s1=20 >>> s2=30 >>> eq='s1*3 - (s2-s1)*1' >>> compiler.parse( eq ) Module(None, Stmt([Discard(Sub((Mul((Name('s1'), Const(3))), Mul((Sub((Name('s2'), Name('s1'))), Const(1))))))]))
Итак, если вы хотите оценить уравнение, как более безопасное чем с помощью
input
Вы можете использоватьcompile
и eval !>>> eq='s1*3 - (s2-s1)*1' >>> a=compile(eq,'','eval') >>> eval(a) 50
Также вы можете использовать
sympy
это библиотека Python для символьной математики. Он стремится стать полнофункциональной системой компьютерной алгебры (CAS), сохраняя при этом код как можно более простым, чтобы быть понятным и легко расширяемым. SymPy написан полностью на Python и не требует никаких внешних библиотек.
Для вычисления выражений можно использовать выражение
NodeVisitor
:from ast import NodeVisitor class EvalVisitor(NodeVisitor): def __init__(self, **kwargs): self._namespace = kwargs def visit_Name(self, node): return self._namespace[node.id] def visit_Num(self, node): return node.n def visit_NameConstant(self, node): return node.value def visit_UnaryOp(self, node): val = self.visit(node.operand) return operators[type(node.op)](val) def visit_BinOp(self, node): lhs = self.visit(node.left) rhs = self.visit(node.right) return operators[type(node.op)](lhs, rhs) def generic_visit(self, node): raise ValueError("malformed node or string: " + repr(node))
Вы можете использовать этот оценщик как
v = EvalVisitor(s1=20, s2=30) print(v.visit(node.body))
Примерно так реализуется
ast.literal_eval
, с добавленной функцией, позволяющей передавать значения в нечисловых выражениях и без них.Хороший трюк с этим
operators
словарь, кстати. Я скопирую это:)
Немного поздно для партии, но для тех, кто заинтересован: это также возможно достичь с небольшой модификацией кода ОП (который, кстати, выглядит удивительно похожим на Этот). Вот оно:
В определение функции
eval_
Добавьте другоеelif
следующим образом:... elif isinstance(node, ast.Name): return operators[node.id] ...
Тогда вы можете просто добавить свои переменные в дикт
operators
. Используя пример OP:>>> s1=20 >>> s2=30 >>> operators['s1']=s1 >>> operators['s2']=s2 >>> node = ast.parse('s1*3 - (s2-s1)*1', mode='eval') >>> eval_(node.body) 50
Реквизит должен перейти к этому ответу, основанному на модуле
asteval
. Также смотритеasteval
Источник .