замените имена переменных фактическими значениями в выражении в 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 3

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 Источник .