Вы можете захотеть полная компиляция в полный объект кода Python, или вы можете просто проанализировать абстрактное синтаксическое дерево.Вы можете использовать функцию compile()
для достижения любого из них или просто использовать ast.parse()
для создания дерева.
Синтаксический анализ в AST токенизирует входные и выходные данныедерево синтаксических объектов, которые вы затем можете анализировать или преобразовывать.Компиляция в байт-код идет еще дальше, используя этот AST для создания объекта кода Python, который вы можете по желанию выполнить либо с помощью eval()
, либо с помощью exec()
function ;обратите внимание, что последний всегда возвращает None
и, вероятно, не лучший выбор для оценки объекта кода выражения.
eval(string)
использует eval(compile(string, "<stdin>", "eval"))
для компиляции строкового аргумента в объект кода, а затем его выполнения,поэтому compile(string, "<stdin>", "eval")
даст вам тот же результат без выполнения.
Используйте "eval"
в качестве режима, если допустимо только выражение , или "exec"
, если полный оператор Python должны быть приняты.compile()
(и ast.parse()
) вызывает исключение SyntaxError
, если ввод не является допустимым выражением Python ("eval"
) или недопустимыми выражениями ("exec"
).
Демонстрация:
>>> example1 = "cos(PARAMPOLY.engineeringValue1) + cos(PARAMPOLY.engineeringValue2)"
>>> example2 = "x + y + z"
>>> compile(example1, "<stdin>", "eval")
<code object <module> at 0x111c2eae0, file "<stdin>", line 1>
>>> compile(example2, "<stdin>", "eval")
<code object <module> at 0x111c2e540, file "<stdin>", line 1>
>>> result2 = _
>>> eval(result2, {"x": 42, "y": 81, "z": 117})
240
>>> compile("not a valid expression", "<stdin>", "eval")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1
not a valid expression
^
SyntaxError: invalid syntax
Синтаксический анализ AST позволил бы вам узнать, к каким именам код должен получить доступ;Вы можете собирать имена, ища Name
узлов:
>>> import ast
>>> tree1 = ast.parse(example1)
>>> tree2 = ast.parse(example2)
>>> ast.dump(tree2.body[0])
"Expr(value=BinOp(left=Call(func=Name(id='cos', ctx=Load()), args=[Attribute(value=Name(id='PARAMPOLY', ctx=Load()), attr='engineeringValue1', ctx=Load())], keywords=[]), op=Add(), right=Call(func=Name(id='cos', ctx=Load()), args=[Attribute(value=Name(id='PARAMPOLY', ctx=Load()), attr='engineeringValue2', ctx=Load())], keywords=[])))"
>>> ast.dump(tree2.body[0])
"Expr(value=BinOp(left=BinOp(left=Name(id='x', ctx=Load()), op=Add(), right=Name(id='y', ctx=Load())), op=Add(), right=Name(id='z', ctx=Load())))"
>>> {node.id for node in ast.walk(tree1) if isinstance(node, ast.Name)}
{'cos', 'PARAMPOLY'}
>>> {node.id for node in ast.walk(tree2) if isinstance(node, ast.Name)}
{'x', 'z', 'y'}
Обратите внимание, что вышеупомянутый игнорируемый контекст, поэтому имя атрибута PARAMPONLY
также указано в списке.Напишите ast.NodeVisitor
подкласс , если вам нужно обработать синтаксическое дерево с дополнительным контекстом.