Deciphering Python: How to use Abstract Syntax Trees (AST) to understand code · Matt Layman

Deciphering Python: How to use Abstract Syntax Trees (AST) to understand code · Matt Layman сад и огород
ast деревья

Deciphering Python: How to use Abstract Syntax Trees (AST) to understand code

Deciphering Python: How to use Abstract Syntax Trees (AST) to understand code · Matt Layman

Как программа на Python (часто называемая интерпретатором) «узнает», как выполнить код? Для новичка в программировании это может показаться волшебством. На самом деле, даже после более чем десяти лет работы в качестве профессионала, мне это все еще кажется волшебством.

Интерпретатор Python — это не волшебство (жаль вас разочаровывать). Он выполняет предсказуемый набор шагов для преобразования кода в исполняемые машиной инструкции.

На достаточно высоком уровне вот что происходит в коде

  1. Код декомпозируется (т. е. разбивается) на список фрагментов, обычно называемых лексемами. Эти лексемы основаны на наборе правил о том, какие вещи должны обрабатываться по-разному. Например, ключевое слово if отличается от числа, такого как 42.
  2. Необработанный список лексем затем преобразуется для создания абстрактного синтаксического дерева (AST). Более подробно об этом рассказывается в этом материале: AST — это коллекция связанных узлов, основанная на синтаксисе языка Python. Если это не имеет смысла, не волнуйтесь, так как позже все будет объяснено более подробно.
  3. From an abstract syntax После создания дерева интерпретатор может генерировать низкоуровневый формат команд, называемый байткодом. Эти команды, такие как BINARY_ADD, очень часто выполняются компьютерами.
  4. Как только команды байткода становятся доступными, интерпретатор может выполнить код. Байткод используется для вызова функций операционной системы и, в конечном счете, для взаимодействия с процессором и памятью для выполнения программы.

Это описание содержит еще много деталей, но это схема того, как входные символы выполняются центральным процессором компьютера.

ASTs as analysis tools

К тому времени, когда исходный код преобразуется в байткод, уже слишком поздно понять, что вы написали. Байткод слишком примитивен и слишком настроен на ускорение работы интерпретатора. Другими словами, байткод предназначен для компьютеров, а не для людей.

On the other hand, abstract syntax Дерево содержит достаточно структурной информации, чтобы помочь изучить код; AST пока не очень удобны для человека, но они имеют больше смысла, чем представления байткода.

Поскольку Python является «аккумуляторным» языком, инструменты, необходимые для использования AST, встроены в стандартную библиотеку.

Основным инструментом для работы с AST является модуль ast. Давайте посмотрим, как это работает на примере.

ast by example

Ниже приведен пример скрипта на Python, который можно использовать. Этот скрипт отвечает на вопрос «Какой модуль был импортирован?». который отвечает на вопрос «Какой модуль был импортирован?».

Импортированоягодицы От.ВывестиИмпортированоПечать  Конечно Главная():  иОткрыть ('ast_example.py', 'r') asИсточник:  ...=ягодицы.Анализ (источник).Читать ( )   Анализатор=Аналитик ()  Анализатор.Посетить (дерево)  Анализатор.Отчетность (дерево)  Класс Аналитик(AST).(nodevisitor):  Конечно__init __(self):  (self): def __init __(self)..Статистика.="Импортировано".: [], "From".: []>   Конечно Visit_import(self, node):  Для.ПсевдонимinУзел.Имя:  (self): def __init __(self)..Статистика [."Импорт".].APPEND (Alias).Name)  (self): def __init __(self)..generic_visit (node)   Конечно visit_importfrom(self, node):  Для.ПсевдонимinУзел.Имя:  (self): def __init __(self)..Статистика [."From".].APPEND (Alias).Name)  (self): def __init __(self)..generic_visit (node)   Конечно Сообщить(self):  pprint (self).(Statistics)  if__Name__.== "__Major__":  Main (main) 

Этот код делает несколько важных вещей:

  1. Он преобразует текст Python-файла (в данном случае, сам код парадигмы) в an abstract syntax tree.
  2. AST и извлекает из него некоторую информацию.

Этот код может быть выполнен следующим образом.

$ python3 ast_example.py 'from'.: ['pprint'.], 'import'.: ['AST'.]> 

Преобразование в AST

иОткрыть ('ast_example.py', 'r') asИсточник:  ...=ягодицы.Анализ (источник).Читать ( ) 

Две строки кода читают файл и создают AST-именованное дерево. функция ast. parse делает это легко! Под капотом этой функции происходит много чего, и ее можно игнорировать как счастье.

После вызова функции Python обработал все лексемы и создал структуру данных (т.е. дерево), содержащую всю необходимую информационную информацию, в соответствии со всеми правилами языка.

Прежде чем продолжить, рассмотрим, что такое дерево. Поскольку деревья — это очень глубокий вопрос в разработке программного обеспечения, рассматривайте это как введение, а не как исчерпывающее описание.

Дерево — это способ хранения данных в виде набора «узлов», связанных с «ребрами».

 +-----+  |a |  +-----+  / \  / \ +-----+ +-----+ |b|c | +-----+ +-----+ 

На этой диаграмме узлами являются A, B и C, а ребро соединяет A с C.

В коде это дерево можно представить следующим образом

Класс Узел:  Конечно__init __(self, value):  (self): def __init __(self)..значение=Значение  (self): def __init __(self)..Дети= []  ...=Узел (A.") дерево.дети.APPEND (NODE (NODE))"B")) дерево.дети.APPEND (NODE (NODE))'C')) 

Обратите внимание, что деревья на самом деле являются узлами! Когда вы используете дерево, вы фактически имеете дело с коллекцией узлов, а переменные дерева — это ссылки на «корневой» узел (например, узел A). Используя этот тип структуры, вы можете управлять каждым узлом в дереве и выполнять над ним действия. Выполните эти действия и получите доступ к каждому узлу дерева для обработки данных.

Конечно print_node_value(значение):  Вывести (значение)  Конечно visit_node_value (value)(node, handle_node):  handle_node (node).Value)  Для.ChildinУзел.Дети:  (узел): visit (child, handle_node)  #Дерево из предыдущего примера. Посетите (дерево, print_node_value). #Это нужно распечатать: # A # B # C 

Зная, что такое дерево, мы можем рассмотреть, что делает следующая часть сценария примера: структура дерева в Python abstract syntax Из-за количества узлов и типа хранимых данных дерево становится более сложным, но основная идея узлов и ребер остается прежней.

Анализ ASTs

После того как дерево создано, аналитик извлекает из него информацию в соответствии с описанными выше шаблонами посещения.

Мы отметили, что AST в Python сложнее, чем основной план узлов. Одно из отличий заключается в том, что в них отслеживаются различные типы узлов. На этом этапе пригодится ast. nodevisitor.

Nodevisitor может реагировать на любой тип узла в Python AST. Чтобы получить доступ к определенному типу узла, вам нужно реализовать метод, похожий на visit_.

В моем примере мы пытаемся узнать об импорте. Чтобы узнать об импорте, код извлекается из узла imports и imports.

Конечно Visit_import(self, node):  Для.ПсевдонимinУзел.Имя:  (self): def __init __(self)..Статистика [."Импорт".].APPEND (Alias).Name)  (self): def __init __(self)..generic_visit (node)  Конечно visit_importfrom(self, node):  Для.ПсевдонимinУзел.Имя:  (self): def __init __(self)..Статистика [."From".].APPEND (Alias).Name)  (self): def __init __(self)..generic_visit (node) 

Этот код берет имя модуля и сохраняет его в списке статистики. Код не отличается фантазией, но показывает, как взаимодействовать с узлами AST.

Для анализа дерева можно использовать класс Nodevisitor.

Анализатор=Аналитик () Анализатор.Посетить (дерево) 

Метод Visit передается в метод Visit_ всякий раз, когда при обходе структуры дерева встречается узел данного типа.

Итак, какие типы узлов существуют, полный список можно найти в разделе Abstract Grammar документации AST. Честно говоря, я нахожу эту документацию немного сложной для восприятия. Возможно, вы добьетесь большего успеха с помощью более усталых руководств, таких как Green Tree Snake Node Driver.

Wrapping up

До сих пор мы надеемся, что understand how to:

  1. Создайте AST из исходного кода Python.
  2. Использовать Nodevisitor для разрешения AST.

Я надеюсь, что вы сможете ответить на многие интересные вопросы о своем коде, используя свой код abstract syntax Дерево. На такие вопросы, как:

  • Сколько переменных вы использовали?
  • Какие функции наиболее часто встречаются в моем коде?
  • Тесно и плотно ли связаны мои секции друг с другом?
  • Какие сторонние библиотеки часто встречаются в разных пакетах?

Возможно, раздел AST не является тем инструментом, к которому вы часто прибегаете, но когда вам нужен AST, его минимальный API очень хорошо запоминается и позволяет быстро проанализировать ваш код!

Если вы нашли этот текст полезным, не могли бы вы поделиться им в Twitter или вашей любимой социальной сети? Пожалуйста, не забывайте писать мне в Twitter по адресу @mblayman, поскольку я люблю обсуждать с людьми подобные вопросы.

Оцените статью