Документация Python неофициальный перевод
Содержание страницы

31.2. ast – Абстрактные синтаксические деревьяast – Abstract Syntax Trees

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


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

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

31.2.1. Классы узловNode classes

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)

31.2.2. Абстрактная грамматикаAbstract Grammar

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

-- ASDL's five builtin types are identifier, int, string, bytes, 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)
          | ClassDef(identifier name, 
             expr* bases,
             keyword* keywords,
             expr? starargs,
             expr? kwargs,
             stmt* body,
             expr* decorator_list)
          | Return(expr? value)

          | Delete(expr* targets)
          | Assign(expr* targets, expr value)
          | AugAssign(expr target, operator op, expr value)

          -- use 'orelse' because else is a keyword in target languages
          | For(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)

          | 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
         | 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,
             expr? starargs, expr? kwargs)
         | Num(object n) -- a number as a PyObject.
         | Str(string s) -- need to specify raw, unicode, etc?
         | Bytes(bytes s)
         | Ellipsis
         -- other literals? bools?

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

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

    arguments = (arg* args, identifier? vararg, expr? varargannotation,
                     arg* kwonlyargs, identifier? kwarg,
                     expr? kwargannotation, expr* defaults,
                     expr* kw_defaults)
    arg = (identifier arg, expr? annotation)

    -- keyword arguments supplied to call
    keyword = (identifier arg, expr value)

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

    withitem = (expr context_expr, expr? optional_vars)
}

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

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

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

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

ast.literal_eval(node_or_string)

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

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

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

ast.get_docstring(node, clean=True)

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

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 обходит AST и использует возвращаемое значение методов обхода для замены или удаления старого узла. Если возвращаемое значение метода обхода равно None, узел удаляется из своего местоположения; в противном случае он заменяется возвращаемым значением. Возвращаемое значение может быть исходным узлом, и в этом случае замена не производится.

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

class RewriteName(NodeTransformer):

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

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

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

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

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

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