- Модуль Python AST для анализа и преобразования кода
- Что такое модуль AST в Python?
- Режим компиляции кода
- Выполнение кода Python
- Оценка выражения
- Создание многострочных AST
- NodeTransformer и NodeVisitor
- Анализ AST
- Использование AST в качестве инструмента анализа
- Когда использовать модуль Python AST?
- Заключение
Модуль Python AST для анализа и преобразования кода
В этом руководстве мы узнаем, как использовать модуль Python AST в работе с кодом.
Что такое модуль AST в Python?
AST в Python означает абстрактное синтаксическое дерево, которое является мощным инструментом языка программирования Python. Он позволяет нам взаимодействовать с самим кодом и изменять его.
Вы когда-нибудь задумывались о том, как запускается код Python? Есть ли за этим магия?
Для тех, кто не знает, интерпретатор Python отвечает за выполнение кода. Он следует заранее написанным руководствам, которые переводят код в инструкции, которые может выполнять машина.
Ниже приведен процесс преобразования кода Python в машинный код:
- Когда мы запускаем код, он разбирается на более мелкие фрагменты, называемые токенами. Токены создаются с помощью предопределенных инструкций, которые должны обрабатываться по-разному. Например, ключевое слово else отличается от числового значения.
- Токены хранятся в списке, преобразованном для построения дерева абстрактного синтаксиса – AST. AST – это набор из двух или более узлов, связанных вместе на основе грамматики языка Python.
- Компилятор может создать инструкцию нижнего уровня, известную как двоичный код, из AST. Этот код общий, поэтому компьютер может легко его обработать.
- Когда интерпретатор получает байтовый код, подобный инструкциям, интерпретатор может запускать этот код. Байт-код отвечает за вызов функции в операционной системе, которая в конечном итоге будет взаимодействовать с ЦП и памятью для запуска программы.
Приведенное выше описание представляет собой грубый набросок того, как интерпретатор запускает код Python с использованием AST.
Режим компиляции кода
Для компиляции кода доступны три режима:
- exec – этот режим используется для выполнения обычного кода Python.
- eval – используется для оценки выражения Python и возвращает результат после оценки.
- single – этот режим работает как оболочка Python, которая выполняет по одному оператору за раз.
Выполнение кода Python
Используя модуль AST, мы можем запустить код Python. Давайте разберемся в следующем примере.
import ast code = ast.parse("print('Hello Learner ! Welcome to JavaTpoint')") print(code) exec(compile(code, filename="", mode="exec"))
Hello Learner! Welcome to JavaTpoint
Оценка выражения
Модуль AST позволяет нам оценивать выражение Python и возвращать результат из выражения.
import ast expression = '6 + 8' code = ast.parse(expression, mode='eval') print(eval(compile(code, '', mode='eval'))) print(ast.dump(code))
14 Expression(body=BinOp(left=Constant(value=6, kind=None), op=Add(), right=Constant(value=8, kind=None)))
Создание многострочных AST
В предыдущем примере мы разобрали однострочные AST и то, как их выгружать. Теперь мы узнаем, как создать многострочное AST.
import ast tree_ast = ast.parse(''' subjects = ['computer science', 'alorithm'] name = 'Ricky' for sub in subjects: print('<> learn <>'.format(name, subjects)) ''') print(ast.dump(tree_ast))
Module(body=[Assign(targets=[Name(id='subjects', ctx=Store())], value=List(elts=[Constant(value='computer science', kind=None), Constant(value='alorithm', kind=None)], ctx=Load()), type_comment=None), Assign(targets=[Name(id='name', ctx=Store())], value=Constant(value='Ricky', kind=None), type_comment=None), For(target=Name(id='fruit', ctx=Store()), iter=Name(id='fruits', ctx=Load()), body=[Expr(value=Call(func=Name(id='print', ctx=Load()), args=[Call(func=Attribute(value=Constant(value='<> learn <>', kind=None), attr='format', ctx=Load()), args=[Name(id='name', ctx=Load()), Name(id='subjects', ctx=Load())], keywords=[])], keywords=[]))], orelse=[], type_comment=None)], type_ignores=[])
NodeTransformer и NodeVisitor
Класс NodeTransformer используется для получения различных типов и модификации в соответствии с нашими требованиями. Модуль AST также предоставляет класс NodeVisitor, который помогает нам вызывать функцию посещения каждый раз, когда мы проходим по дереву. Чтобы мы могли получить больший контроль над узлами, давайте разберемся в следующем примере.
import ast class Visitor(ast.NodeVisitor): def visit_Str(self, node): print('String Node: "' + node.s + '"') class MyTransformer(ast.NodeTransformer): def visit_Str(self, node): return ast.Str('str: ' + node.s) parsed = ast.parse("print('Hello World')") MyTransformer().visit(parsed) Visitor().visit(parsed)
Welcome to the Javatpoint
В приведенном выше коде мы импортировали модуль ast, который анализирует код. Затем мы определили класс Visitor, который наследует класс NodeVisitor и каждый раз находит строковый узел; он трансформируется, добавляя префикс.
Мы также можем использовать модуль, когда запускаем напрямую исходный код. Давайте разберемся в следующем примере.
import ast from pprint import pprint def main(): with open("ast_module.py", "r") as source: ast_tree = ast.parse(source.read()) analysis = Analyzer() analysis.visit(ast_tree) analysis.report() class Analyzer(ast.NodeVisitor): def __init__(self): self.stats = def node_visit(self, node): for alias in node.names: self.stats["import"].append(alias.name) self.generic_visit(node) def node_visitFrom(self, node): for alias in node.names: self.stats["from"].append(alias.name) self.generic_visit(node) def report(self): pprint(self.stats) if __name__ == "__main__":
Приведенный выше код преобразует файл Python в абстрактное синтаксическое дерево. Затем мы анализируем дерево, чтобы получить полезную информацию.
Мы открыли файл Python в режиме чтения, а затем создали AST с именем ast_tree. Затем функция parse() обработала все токены, соблюдала все языковые правила и построила древовидную структуру данных, состоящую из большого количества полезной информации.
Дерево – это не что иное, как набор узлов, где переменная дерева ссылается на «корневой» узел. Таким образом, мы можем посещать каждый узел в дереве и выполнять операции. Но сначала мы посещаем каждый узел и обрабатываем данные.
Анализ AST
Как только мы получили дерево, теперь Анализатор следует шаблону посетителя. Используя класс NodeVisitor, мы можем отслеживать любой узел в Python. Нам нужно реализовать метод visit_ для посещения конкретного типа узла. В предыдущем примере мы использовали приведенный ниже сценарий.
class Analyzer(ast.NodeVisitor): def node_visit(self, node): for alias in node.names: self.stats["import"].append(alias.name) self.generic_visit(node) def node_visitFrom(self, node): for alias in node.names: self.stats["from"].append(alias.name) self.generic_visit(node)
Код принимает имя модуля и сохраняет его в списке статистики. С помощью класса NodeVisitor мы можем проанализировать дерево.
analyzer = Analyzer() analyzer.visit(tree)
Метод visit() будет работать так же, как метод visit_.
Использование AST в качестве инструмента анализа
После того, как код Python превратился в байтовый код, он не может быть прочитан людьми. Но это делает интерпретатор быстрым, а это значит, что байт-код предназначен для машины, а не для людей.
AST состоит из достаточно структурированной информации, что делает их полезными при изучении кода Python. Однако AST по-прежнему неудобны для пользователя, но они более понятны, чем представление байтового кода.
Когда использовать модуль Python AST?
Абстрактное дерево синтаксиса очень полезно для инструментов покрытия кода. Оно анализирует исходный код и находит возможные недостатки и ошибки в коде. Его также можно использовать:
- как собственный интерпретатор Python.
- для анализа статического кода.
- чтобы сделать IDE интеллектуальными, известными как IntelliSense.
Заключение
Мы узнали о модуле AST в Python, который отвечает за выполнение кода. Затем мы построили дерево AST из кода и провели анализ с помощью класса NodeVisitor.
Источник