Spec-Zone.ru › Python 3.7

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, который приведён ниже. Они определены в модуле C _ast и повторно экспортированы в ast.

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

_fields

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

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

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

lineno
col_offset

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

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

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

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

node = ast.UnaryOp()
node.op = ast.USub()
node.operand = ast.Num()
node.operand.n = 5
node.operand.lineno = 0
node.operand.col_offset = 0
node.lineno = 0
node.col_offset = 0

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

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

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

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

-- ASDL's 7 builtin types are:
-- identifier, int, string, bytes, object, singleton, constant
--
-- singleton: None, True or False
-- constant can be None, whereas None means "no value" for object.

module Python
{
    mod = Module(stmt* body)
        | Interactive(stmt* body)
        | Expression(expr body)

        -- 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)
          | AsyncFunctionDef(identifier name, arguments args,
                             stmt* body, expr* decorator_list, expr? returns)

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

          | Delete(expr* targets)
          | Assign(expr* targets, expr value)
          | 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)
          | AsyncFor(expr target, expr iter, stmt* body, stmt* orelse)
          | While(expr test, stmt* body, stmt* orelse)
          | If(expr test, stmt* body, stmt* orelse)
          | With(withitem* items, stmt* body)
          | AsyncWith(withitem* items, stmt* body)

          | 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)

          -- BoolOp() can use left & right?
    expr = BoolOp(boolop op, expr* values)
         | 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)
         | Num(object n) -- a number as a PyObject.
         | Str(string s) -- need to specify raw, unicode, etc?
         | FormattedValue(expr value, int? conversion, expr? format_spec)
         | JoinedStr(expr* values)
         | Bytes(bytes s)
         | NameConstant(singleton value)
         | Ellipsis
         | Constant(constant value)

         -- 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)

    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)

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

    arg = (identifier arg, expr? annotation)
           attributes (int lineno, int 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)
}

Вспомогательные функции ast

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

ast.parse(source, filename='<unknown>', mode='exec')

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

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

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

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 имеет значение true, отформатируйте строку документации с помощью inspect.cleandoc().

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

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) из 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), который позволяет вносить изменения.

class ast.NodeTransformer

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

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

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

class RewriteName(NodeTransformer):

    def visit_Name(self, node):
        return Subscript(
            value=Name(id='data', ctx=Load()),
            slice=Index(value=Str(s=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.

См. также

Green Tree Snakes, внешний ресурс документации, содержит подробные сведения о работе с AST Python.

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

Spec-Zone.ru

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