Spec-Zone.ru › Python 3.6

ast — Деревья абстрактного синтаксиса

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

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

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

32.2.1. Классы узлов

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

Экземпляры классов 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)

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

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

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

32.2.3. Инструменты для работы с 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, ClassDef или Module), или None если у него нет строки документации. Если clean равно True, очищает отступы в строке документации с помощью 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 будет обходить АСТ и использовать возвращаемое значение методов посетителя для замены или удаления старого узла. Если возвращаемое значение метода посетителя равно 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.

См. также

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

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

Spec-Zone.ru

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