Spec-Zone.ru › Python 3.8

ast — Абстрактные синтаксические деревья

Исходный код: Lib/ast.py

Модуль ast помогает приложениям Python обрабатывать деревья абстрактной синтаксической грамматики Python. Сама абстрактная синтаксическая грамматика может изменяться с каждой версией Python; этот модуль помогает программно определить, как выглядит текущая грамматика.

Абстрактное синтаксическое дерево можно сгенерировать, передав ast.PyCF_ONLY_AST в качестве флага встроенной функции compile() или используя вспомогательную функцию parse() из этого модуля. Результатом будет дерево объектов, классы которых наследуются от ast.AST. Абстрактное синтаксическое дерево можно скомпилировать в объект кода Python, используя встроенную функцию compile().

Классы узлов

class ast.AST

Это базовый класс для всех классов узлов AST. Фактические классы узлов производятся из файла Parser/Python.asdl, который воспроизведён ниже. Они определены в модуле _ast C и повторно экспортированы в ast.

Для каждого левого символа в абстрактной грамматике (например, ast.stmt или ast.expr), определён один класс. Кроме того, для каждого конструктора в правой части определён один класс; эти классы наследуют от классов для левых частей деревьев. Например, ast.BinOp наследуется от ast.expr. Для правил производства с альтернативами (также известными как «суммы»), класс левой части является абстрактным: создаются только экземпляры конкретных узлов конструкторов.

_fields

Каждый конкретный класс имеет атрибут _fields, который содержит имена всех дочерних узлов.

Каждый экземпляр конкретного класса имеет по одному атрибуту для каждого дочернего узла, соответствующего типу, определённому в грамматике. Например, экземпляры ast.BinOp имеют атрибут left типа ast.expr.

Если эти атрибуты помечены как необязательные в грамматике (с использованием вопросительного знака), их значение может быть None. Если атрибуты могут принимать нулевое или более значений (помечены звёздочкой), значения представлены в виде списков Python. Все возможные атрибуты должны присутствовать и иметь допустимые значения при компиляции AST с помощью compile().

lineno
col_offset
end_lineno
end_col_offset

Экземпляры классов ast.expr и ast.stmt имеют атрибуты lineno, col_offset, lineno и col_offset. Атрибуты lineno и end_lineno представляют номера первой и последней строки в тексте исходного кода (с индексацией с 1, первая строка — строка 1), а атрибуты col_offset и end_col_offset — соответствующие смещения байтов в кодировке UTF-8 для первых и последних токенов, которые сгенерировали узел. Смещение в UTF-8 записывается, потому что парсер использует UTF-8 в своей работе.

Обратите внимание, что конечные позиции не требуются компилятором и поэтому являются необязательными. Конечное смещение находится после последнего символа, например, вы можете получить сегмент исходного кода узла однострочного выражения с помощью source_line[node.col_offset : node.end_col_offset].

Конструктор класса ast.T анализирует его аргументы следующим образом:

  • Если есть позиционные аргументы, их должно быть столько же, сколько элементов в T._fields; они будут присвоены в качестве атрибутов с соответствующими именами.
  • Если есть именованные аргументы, они будут устанавливать атрибуты с теми же именами в соответствии с указанными значениями.

Например, чтобы создать и заполнить узел ast.UnaryOp, можно использовать

node = ast.UnaryOp()
node.op = ast.USub()
node.operand = ast.Constant()
node.operand.value = 5
node.operand.lineno = 0
node.operand.col_offset = 0
node.lineno = 0
node.col_offset = 0

или более компактный вариант

node = ast.UnaryOp(ast.USub(), ast.Constant(5, lineno=0, col_offset=0),
                   lineno=0, col_offset=0)

Изменено в версии 3.8: Класс ast.Constant теперь используется для всех констант.

Устарело начиная с версии 3.8: Старые классы ast.Num, ast.Str, ast.Bytes, ast.NameConstant и ast.Ellipsis всё ещё доступны, но они будут удалены в будущих версиях Python. В то же время, их создание вернёт экземпляр другого класса.

Абстрактная грамматика

Абстрактная грамматика в настоящее время определена следующим образом:

-- ASDL's 5 builtin types are:
-- identifier, int, string, object, constant

module Python
{
    mod = Module(stmt* body, type_ignore *type_ignores)
        | Interactive(stmt* body)
        | Expression(expr body)
        | FunctionType(expr* argtypes, expr returns)

        -- not really an actual node but useful in Jython's typesystem.
        | Suite(stmt* body)

    stmt = FunctionDef(identifier name, arguments args,
                       stmt* body, expr* decorator_list, expr? returns,
                       string? type_comment)
          | AsyncFunctionDef(identifier name, arguments args,
                             stmt* body, expr* decorator_list, expr? returns,
                             string? type_comment)

          | ClassDef(identifier name,
             expr* bases,
             keyword* keywords,
             stmt* body,
             expr* decorator_list)
          | Return(expr? value)

          | Delete(expr* targets)
          | Assign(expr* targets, expr value, string? type_comment)
          | AugAssign(expr target, operator op, expr value)
          -- 'simple' indicates that we annotate simple name without parens
          | AnnAssign(expr target, expr annotation, expr? value, int simple)

          -- use 'orelse' because else is a keyword in target languages
          | For(expr target, expr iter, stmt* body, stmt* orelse, string? type_comment)
          | AsyncFor(expr target, expr iter, stmt* body, stmt* orelse, string? type_comment)
          | While(expr test, stmt* body, stmt* orelse)
          | If(expr test, stmt* body, stmt* orelse)
          | With(withitem* items, stmt* body, string? type_comment)
          | AsyncWith(withitem* items, stmt* body, string? type_comment)

          | Raise(expr? exc, expr? cause)
          | Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)
          | Assert(expr test, expr? msg)

          | Import(alias* names)
          | ImportFrom(identifier? module, alias* names, int? level)

          | Global(identifier* names)
          | Nonlocal(identifier* names)
          | Expr(expr value)
          | Pass | Break | Continue

          -- XXX Jython will be different
          -- col_offset is the byte offset in the utf8 string the parser uses
          attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset)

          -- BoolOp() can use left & right?
    expr = BoolOp(boolop op, expr* values)
         | NamedExpr(expr target, expr value)
         | BinOp(expr left, operator op, expr right)
         | UnaryOp(unaryop op, expr operand)
         | Lambda(arguments args, expr body)
         | IfExp(expr test, expr body, expr orelse)
         | Dict(expr* keys, expr* values)
         | Set(expr* elts)
         | ListComp(expr elt, comprehension* generators)
         | SetComp(expr elt, comprehension* generators)
         | DictComp(expr key, expr value, comprehension* generators)
         | GeneratorExp(expr elt, comprehension* generators)
         -- the grammar constrains where yield expressions can occur
         | Await(expr value)
         | Yield(expr? value)
         | YieldFrom(expr value)
         -- need sequences for compare to distinguish between
         -- x < 4 < 3 and (x < 4) < 3
         | Compare(expr left, cmpop* ops, expr* comparators)
         | Call(expr func, expr* args, keyword* keywords)
         | FormattedValue(expr value, int? conversion, expr? format_spec)
         | JoinedStr(expr* values)
         | Constant(constant value, string? kind)

         -- the following expression can appear in assignment context
         | Attribute(expr value, identifier attr, expr_context ctx)
         | Subscript(expr value, slice slice, expr_context ctx)
         | Starred(expr value, expr_context ctx)
         | Name(identifier id, expr_context ctx)
         | List(expr* elts, expr_context ctx)
         | Tuple(expr* elts, expr_context ctx)

          -- col_offset is the byte offset in the utf8 string the parser uses
          attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset)

    expr_context = Load | Store | Del | AugLoad | AugStore | Param

    slice = Slice(expr? lower, expr? upper, expr? step)
          | ExtSlice(slice* dims)
          | Index(expr value)

    boolop = And | Or

    operator = Add | Sub | Mult | MatMult | Div | Mod | Pow | LShift
                 | RShift | BitOr | BitXor | BitAnd | FloorDiv

    unaryop = Invert | Not | UAdd | USub

    cmpop = Eq | NotEq | Lt | LtE | Gt | GtE | Is | IsNot | In | NotIn

    comprehension = (expr target, expr iter, expr* ifs, int is_async)

    excepthandler = ExceptHandler(expr? type, identifier? name, stmt* body)
                    attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset)

    arguments = (arg* posonlyargs, arg* args, arg? vararg, arg* kwonlyargs,
                 expr* kw_defaults, arg? kwarg, expr* defaults)

    arg = (identifier arg, expr? annotation, string? type_comment)
           attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset)

    -- keyword arguments supplied to call (NULL identifier for **kwargs)
    keyword = (identifier? arg, expr value)

    -- import name with optional 'as' alias.
    alias = (identifier name, identifier? asname)

    withitem = (expr context_expr, expr? optional_vars)

    type_ignore = TypeIgnore(int lineno, string tag)
}

Помощники AST

Помимо классов узлов, модуль ast определяет эти служебные функции и классы для обхода абстрактных синтаксических деревьев:

ast.parse(source, filename='<unknown>', mode='exec', *, type_comments=False, feature_version=None)

Разбор исходного кода в узел AST. Эквивалентно compile(source, filename, mode, ast.PyCF_ONLY_AST).

Если type_comments=True задано, анализатор модифицируется для проверки и возврата комментариев типов, как указано в PEP 484 и PEP 526. Это эквивалентно добавлению ast.PyCF_TYPE_COMMENTS к флагам, переданным compile(). Это будет сообщать об ошибках синтаксиса для неправильно размещенных комментариев типов. Без этого флага комментарии типов будут игнорироваться, и поле type_comment для выбранных узлов AST всегда будет None. Кроме того, расположения комментариев # type: ignore будут возвращены как атрибут type_ignores узла Module (в противном случае это всегда пустой список).

Кроме того, если mode равно 'func_type', синтаксис входных данных изменяется, чтобы соответствовать «комментариям типов сигнатуры» PEP 484, например, (str, int) -> List[str].

Также, установка feature_version в кортеж (major, minor) попытается выполнить разбор с помощью грамматики указанной версии Python. В настоящее время major должно быть равно 3. Например, установка feature_version=(3, 4) позволит использовать async и await в качестве имён переменных. Наименьшая поддерживаемая версия — (3, 4); наибольшая — sys.version_info[0:2].

Предупреждение

Возможна ошибка интерпретатора Python при достаточно большой/сложной строке из-за ограничений глубины стека в компиляторе AST Python.

Изменено в версии 3.8: Добавлены type_comments, mode='func_type' и feature_version.

ast.literal_eval(node_or_string)

Безопасно оценить узел выражения или строку, содержащую Python-литерал или отображение контейнера. Предоставленная строка или узел могут содержать только следующие структуры Python-литералов: строки, байты, числа, кортежи, списки, словари, множества, булевы значения и None.

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

Предупреждение

Возможна ошибка интерпретатора Python при достаточно большой/сложной строке из-за ограничений глубины стека в компиляторе AST Python.

Изменено в версии 3.2: Теперь поддерживает байты и множества.

ast.get_docstring(node, clean=True)

Возвращает строку документации данного узла node (который должен быть узлом FunctionDef, AsyncFunctionDef, ClassDef, или Module), или None если у него нет строки документации. Если clean истинно, отформатировать отступ строки документации с помощью inspect.cleandoc().

Изменено в версии 3.5: AsyncFunctionDef теперь поддерживается.

ast.get_source_segment(source, node, *, padded=False)

Получить фрагмент исходного кода source, который сгенерировал node. Если некоторая информация о расположении (lineno, end_lineno, col_offset, или end_col_offset ) отсутствует, возвращается None.

Если padded равно True, первая строка многострочного оператора будет дополнена пробелами, чтобы соответствовать ее первоначальному положению.

Введено в версии 3.8.

ast.fix_missing_locations(node)

При компиляции дерева узлов с помощью compile(), компилятор ожидает атрибуты lineno и col_offset для каждого узла, который их поддерживает. Это довольно утомительно заполнять для сгенерированных узлов, поэтому этот помощник добавляет эти атрибуты рекурсивно, где они ещё не заданы, устанавливая их в значения родительского узла. Он работает рекурсивно, начиная с узла node.

ast.increment_lineno(node, n=1)

Увеличить номер строки и конечный номер строки каждого узла в дереве, начиная с узла node на n. Это полезно для «перемещения кода» в другое место в файле.

ast.copy_location(new_node, old_node)

Скопировать расположение источника (lineno, col_offset, end_lineno, и end_col_offset) из old_node в new_node, если это возможно, и вернуть new_node.

ast.iter_fields(node)

Возвращает кортеж (fieldname, value) для каждого поля в node._fields , которое присутствует в узле node.

ast.iter_child_nodes(node)

Возвращает все дочерние узлы узла node, то есть все поля, которые являются узлами, и все элементы полей, которые являются списками узлов.

ast.walk(node)

Рекурсивно возвращает все дочерние узлы в дереве, начиная с узла node (включая сам node), в произвольном порядке. Это полезно, если вы хотите только изменить узлы на месте и не заботитесь о контексте.

class ast.NodeVisitor

Базовый класс посетителя узлов, который обходит абстрактное синтаксическое дерево и вызывает функцию посетителя для каждого найденного узла. Эта функция может вернуть значение, которое передается методом visit().

Этот класс предназначен для наследования, при этом подкласс добавляет методы посетителей.

visit(node)

Посетить узел. По умолчанию вызывает метод, названный self.visit_classname , где classname — имя класса узла, или generic_visit(), если такого метода нет.

generic_visit(node)

Этот посетитель вызывает visit() для всех дочерних узлов узла.

Обратите внимание, что дочерние узлы узлов, у которых есть пользовательский метод посетителя, не будут посещены, если посетитель не вызовет generic_visit() или не посетит их самостоятельно.

Не используйте NodeVisitor, если хотите внести изменения в узлы во время обхода. Для этого существует специальный посетитель (NodeTransformer), который позволяет вносить изменения.

Устарело начиная с версии 3.8: Методы visit_Num(), visit_Str(), visit_Bytes(), visit_NameConstant() и visit_Ellipsis() устарели и не будут вызываться в будущих версиях Python. Добавьте метод visit_Constant() для обработки всех постоянных узлов.

class ast.NodeTransformer

Подкласс NodeVisitor, который обходит абстрактное синтаксическое дерево и позволяет изменять узлы.

Преобразователь NodeTransformer пройдёт по AST и использует возвращаемое значение методов посетителя для замены или удаления старого узла. Если возвращаемое значение метода посетителя равно None, узел будет удален из своего местоположения, иначе он будет заменён возвращаемым значением. Возвращаемое значение может быть исходным узлом, в этом случае замена не выполняется.

Вот пример преобразователя, который переписывает все случаи поиска имен (foo) на data['foo']:

class RewriteName(NodeTransformer):

    def visit_Name(self, node):
        return Subscript(
            value=Name(id='data', ctx=Load()),
            slice=Index(value=Constant(value=node.id)),
            ctx=node.ctx
        )

Помните, что если у узла, с которым вы работаете, есть дочерние узлы, вы должны либо преобразовать дочерние узлы самостоятельно, либо сначала вызвать метод generic_visit() для узла.

Для узлов, которые были частью коллекции операторов (это относится ко всем узлам операторов), посетитель также может возвращать список узлов, а не только один узел.

Если NodeTransformer вводит новые узлы (которые не были частью исходного дерева) без указания информации о местоположении (например, lineno), следует вызвать fix_missing_locations() с новым поддеревом, чтобы пересчитать информацию о местоположении:

tree = ast.parse('foo', mode='eval')
new_tree = fix_missing_locations(RewriteName().visit(tree))

Обычно преобразователь используется так:

node = YourTransformer().visit(node)
ast.dump(node, annotate_fields=True, include_attributes=False)

Возвращает отформатированный вывод дерева в узле node. Это в основном полезно для отладки. Если annotate_fields истинно (по умолчанию), возвращаемая строка покажет имена и значения полей. Если annotate_fields ложно, результирующая строка будет более компактной, опуская недвусмысленные имена полей. Атрибуты, такие как номера строк и смещения столбцов, по умолчанию не выводятся. Если это нужно, можно установить include_attributes в значение true.

См. также

Зелёные древесные змеи, внешний ресурс документации, содержит подробные сведения о работе с Python AST.

ASTTokens добавляет в Python AST позиции токенов и текст из исходного кода, который их породил. Это полезно для инструментов, выполняющих преобразования исходного кода.

leoAst.py объединяет основанные на токенах и на синтаксических деревьях представления python-программ, вставляя двусторонние связи между токенами и узлами ast.

LibCST анализирует код как дерево конкретной синтаксической структуры (Concrete Syntax Tree), которое выглядит как дерево ast и сохраняет все детали форматирования. Это полезно для создания автоматизированных приложений для переработки кода (codemod) и линтеров.

Parso — это Python-парсер, который поддерживает восстановление ошибок и двусторонний разбор для разных версий Python (в нескольких версиях Python). Parso также может перечислить несколько синтаксических ошибок в вашем python-файле.

© 2001–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.8/library/ast.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API