ast --- 抽象語法樹 (Abstract Syntax Trees)

原始碼:Lib/ast.py


ast 模組可以幫助 Python 應用程式處理 Python 抽象語法文法 (abstract syntax grammar) 樹狀資料結構。抽象語法本身可能會隨著每個 Python 版本發布而改變;此模組有助於以程式化的方式來得知目前文法的面貌。

要生成抽象語法樹,可以透過將 ast.PyCF_ONLY_AST 作為旗標傳遞給內建函式 compile() 或使用此模組所提供的 parse() 輔助函式。結果將會是一個物件的樹,其類別都繼承自 ast.AST。可以使用內建的 compile() 函式將抽象語法樹編譯成 Python 程式碼物件。

抽象文法 (Abstract Grammar)

抽象文法目前定義如下:

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

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

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

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

          | Delete(expr* targets)
          | Assign(expr* targets, expr value, string? type_comment)
          | TypeAlias(expr name, type_param* type_params, 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, 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)

          | Match(expr subject, match_case* cases)

          | Raise(expr? exc, expr? cause)
          | Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)
          | TryStar(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

          -- 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)
         | Interpolation(expr value, constant str, int conversion, expr? format_spec)
         | JoinedStr(expr* values)
         | TemplateStr(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, expr 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)

         -- can appear only in Subscript
         | Slice(expr? lower, expr? upper, expr? step)

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

    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)
               attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset)

    -- import name with optional 'as' alias.
    alias = (identifier name, identifier? asname)
             attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset)

    withitem = (expr context_expr, expr? optional_vars)

    match_case = (pattern pattern, expr? guard, stmt* body)

    pattern = MatchValue(expr value)
            | MatchSingleton(constant value)
            | MatchSequence(pattern* patterns)
            | MatchMapping(expr* keys, pattern* patterns, identifier? rest)
            | MatchClass(expr cls, pattern* patterns, identifier* kwd_attrs, pattern* kwd_patterns)

            | MatchStar(identifier? name)
            -- The optional "rest" MatchMapping parameter handles capturing extra mapping keys

            | MatchAs(pattern? pattern, identifier? name)
            | MatchOr(pattern* patterns)

             attributes (int lineno, int col_offset, int end_lineno, int end_col_offset)

    type_ignore = TypeIgnore(int lineno, string tag)

    type_param = TypeVar(identifier name, expr? bound, expr? default_value)
               | ParamSpec(identifier name, expr? default_value)
               | TypeVarTuple(identifier name, expr? default_value)
               attributes (int lineno, int col_offset, int end_lineno, int end_col_offset)
}

節點 (Node) 類別

class ast.AST

這是所有 AST 節點類別的基礎。實際的節點類別是衍生自 Parser/Python.asdl 檔案,該檔案在上方 重現。它們被定義於 _ast 的 C 模組中,並於 ast 中重新匯出。

抽象文法中為每個左側符號定義了一個類別(例如 ast.stmtast.expr)。此外,也為每個右側的建構函式 (constructor) 定義了一個類別;這些類別繼承自左側樹的類別。例如,ast.BinOp 繼承自 ast.expr。對於具有替代方案(即為「和 (sums)」)的生產規則,左側類別是抽象的:僅有特定建構函式節點的實例會被建立。

_fields

每個具體類別都有一個屬性 _fields,它會給出所有子節點的名稱。

具體類別的每個實例對於每個子節點都有一個屬性,其型別如文法中所定義。例如,ast.BinOp 實例具有型別為 ast.expr 的屬性 left

如果這些屬性在文法中被標記為可選(使用問號),則該值可能為 None。如果屬性可以有零個或多個值(用星號標記),則這些值將表示為 Python 串列。使用 compile() 編譯 AST 時,所有可能的屬性都必須存在並且具有有效值。

_field_types

每個具體類別上的 _field_types 屬性是將欄位名稱(也在 _fields 中列出)對映到其型別的字典。

>>> ast.TypeVar._field_types
{'name': <class 'str'>, 'bound': ast.expr | None, 'default_value': ast.expr | None}

在 3.13 版被加入.

lineno
col_offset
end_lineno
end_col_offset

ast.exprast.stmt 子類別的實例具有 linenocol_offsetend_linenoend_col_offset 屬性。linenoend_lineno 是原始文本跨度 (source text span) 的第一個和最後一個列號(1-indexed,因此第一列號是 1)以及 col_offsetend_col_offset 是生成節點的第一個和最後一個標記對應的 UTF-8 位元組偏移量。會記錄 UTF-8 偏移量是因為剖析器 (parser) 內部使用 UTF-8。

請注意,編譯器並不需要結束位置,因此其為可選的。結束偏移量在最後一個符號之後,例如可以使用 source_line[node.col_offset : node.end_col_offset] 來取得單列運算式節點 (expression node) 的原始片段。

ast.T 類別的建構函式按以下方式剖析其引數:

  • 如果有位置引數,則必須與 T._fields 中的項目一樣多;它們將被賦値為這些名稱的屬性。

  • 如果有關鍵字引數,它們會將相同名稱的屬性設定為給定值。

例如,要建立並填充 (populate) ast.UnaryOp 節點,你可以使用:

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

如果建構函式中省略了文法中可選的欄位,則它預設為 None。如果省略串列欄位,則預設為空串列。如果省略 ast.expr_context 型別的欄位,則預設為 Load()。如果省略任何其他欄位,則會引發 DeprecationWarning,且 AST 節點將沒有此欄位。在 Python 3.15 中,這種情況會引發錯誤。

在 3.8 版的變更: ast.Constant 類別現在用於所有常數。

在 3.9 版的變更: 以它們的值表示簡單索引,擴充切片 (slice) 則以元組 (tuple) 表示。

在 3.13 版的變更: AST node constructors were changed to provide sensible defaults for omitted fields: optional fields now default to None, list fields default to an empty list, and fields of type ast.expr_context default to Load(). Previously, omitted attributes would not exist on constructed nodes (accessing them raised AttributeError).

在 3.14 版的變更: AST 節點的 __repr__() 輸出包含節點欄位的值。

自從版本 3.8 後不推薦使用,已從版本 3.14 中移除。: 過去的 Python 版本提供了 AST 類別 ast.Numast.Strast.Bytesast.NameConstantast.Ellipsis,這些類別在 Python 3.8 中已被棄用。這些類別在 Python 3.14 中被移除,其功能已被 ast.Constant 取代。

在 3.9 版之後被棄用: 舊的類別 ast.Indexast.ExtSlice 仍然可用,但它們將在未來的 Python 版本中刪除。同時,實例化它們會回傳不同類別的實例。

自從版本 3.13 後不推薦使用,將會自版本 3.15 中移除。: 先前版本的 Python 允許建立缺少必填欄位的 AST 節點。同樣地,AST 節點建構函式允許將任意關鍵字引數設為 AST 節點的屬性,即使它們與 AST 節點的任何欄位都不匹配。此行為已被棄用,並將在 Python 3.15 中刪除。

備註

這裡顯示的特定節點類別的描述最初是從出色的 Green Tree Snakes 專案和所有貢獻者那裡改編而來的。

根節點

class ast.Module(body, type_ignores)

一個 Python 模組,與檔案輸入 一樣。由 ast.parse() 在預設的 "exec" mode 下生成的節點型別。

body 是模組的陳述式 的一個 list

type_ignores 是模組的忽略型別註解的 list;有關更多詳細資訊,請參閱 ast.parse()

>>> print(ast.dump(ast.parse('x = 1'), indent=4))
Module(
    body=[
        Assign(
            targets=[
                Name(id='x', ctx=Store())],
            value=Constant(value=1))])
class ast.Expression(body)

單個 Python 運算式輸入。當 mode"eval" 時節點型別由 ast.parse() 生成。

body 是單個節點,是運算式型別的其中之一。

>>> print(ast.dump(ast.parse('123', mode='eval'), indent=4))
Expression(
    body=Constant(value=123))
class ast.Interactive(body)

單個互動式輸入,和互動模式中所述的相似。當 mode"single" 時節點型別由 ast.parse() 生成。

body陳述式節點 (statement nodes)list

>>> print(ast.dump(ast.parse('x = 1; y = 2', mode='single'), indent=4))
Interactive(
    body=[
        Assign(
            targets=[
                Name(id='x', ctx=Store())],
            value=Constant(value=1)),
        Assign(
            targets=[
                Name(id='y', ctx=Store())],
            value=Constant(value=2))])
class ast.FunctionType(argtypes, returns)

函式的舊式型別註解的表示法,因為 3.5 之前的 Python 版本不支援 PEP 484 註釋。當 mode"func_type" 時節點型別由 ast.parse() 生成。

這種型別的註解看起來像這樣:

def sum_two_number(a, b):
    # type: (int, int) -> int
    return a + b

argtypes運算式節點list

returns 是單個運算式節點

>>> print(ast.dump(ast.parse('(int, str) -> List[int]', mode='func_type'), indent=4))
FunctionType(
    argtypes=[
        Name(id='int', ctx=Load()),
        Name(id='str', ctx=Load())],
    returns=Subscript(
        value=Name(id='List', ctx=Load()),
        slice=Name(id='int', ctx=Load()),
        ctx=Load()))

在 3.8 版被加入.

文本 (Literals)

class ast.Constant(value)

一個常數值。Constant 文本的 value 屬性包含它所代表的 Python 物件。表示的值可以是 strbytesintfloatcomplexbool 的實例,以及常數 NoneEllipsis

>>> print(ast.dump(ast.parse('123', mode='eval'), indent=4))
Expression(
    body=Constant(value=123))
class ast.FormattedValue(value, conversion, format_spec)

表示 f 字串 (f-string) 中的單個格式化欄位的節點。如果字串包含單個格式欄位並且沒有其他內容,則可以隔離 (isolate) 該節點,否則它將出現在 JoinedStr 中。

  • value 為任何運算式節點(例如文字、變數或函式呼叫)。

  • conversion 是一個整數:

    • -1: 無格式化

    • 97 (ord('a')):!a ASCII 格式化

    • 114 (ord('r')):!r repr() 格式化

    • 115 (ord('s')):!s string 格式化

  • format_spec 是一個 JoinedStr 節點,表示值的格式,若未指定格式則為 Noneconversionformat_spec 可以同時設定。

class ast.JoinedStr(values)

一個 f 字串,包含一系列 FormattedValueConstant 節點。

>>> print(ast.dump(ast.parse('f"sin({a}) is {sin(a):.3}"', mode='eval'), indent=4))
Expression(
    body=JoinedStr(
        values=[
            Constant(value='sin('),
            FormattedValue(
                value=Name(id='a', ctx=Load()),
                conversion=-1),
            Constant(value=') is '),
            FormattedValue(
                value=Call(
                    func=Name(id='sin', ctx=Load()),
                    args=[
                        Name(id='a', ctx=Load())]),
                conversion=-1,
                format_spec=JoinedStr(
                    values=[
                        Constant(value='.3')]))]))
class ast.TemplateStr(values, /)

在 3.14 版被加入.

表示模板字串字面值的節點,由一系列 InterpolationConstant 節點組成。這些節點可以是任意順序,不需要交錯排列。

>>> expr = ast.parse('t"{name} finished {place:ordinal}"', mode='eval')
>>> print(ast.dump(expr, indent=4))
Expression(
    body=TemplateStr(
        values=[
            Interpolation(
                value=Name(id='name', ctx=Load()),
                str='name',
                conversion=-1),
            Constant(value=' finished '),
            Interpolation(
                value=Name(id='place', ctx=Load()),
                str='place',
                conversion=-1,
                format_spec=JoinedStr(
                    values=[
                        Constant(value='ordinal')]))]))
class ast.Interpolation(value, str, conversion, format_spec=None)

在 3.14 版被加入.

表示模板字串字面值中單一插值欄位的節點。

  • value 為任何運算式節點(例如文字、變數或函式呼叫)。這和 FormattedValue.value 的意思相同。

  • str 是一個包含插值運算式文字的常數。

    如果 str 被設為 None,則在呼叫 ast.unparse() 時會使用 value 來產生程式碼。這不再保證產生的程式碼與原始程式碼相同,且適用於程式碼產生。

  • conversion 是一個整數:

    • -1:無規範

    • 97 (ord('a')):!a ASCII 轉換

    • 114 (ord('r')):!r repr() 轉換

    • 115 (ord('s')):!s string 轉換

    這與 FormattedValue.conversion 的意思相同。

  • format_spec 是一個 JoinedStr 節點,表示值的格式,若未指定格式則為 Noneconversionformat_spec 可以同時設定。這與 FormattedValue.format_spec 的意思相同。

class ast.List(elts, ctx)
class ast.Tuple(elts, ctx)

串列或元組。elts 保存表示元素的節點串列。如果容器是賦值目標(即 (x,y)=something ),則 ctxStore,否則是 Load

>>> print(ast.dump(ast.parse('[1, 2, 3]', mode='eval'), indent=4))
Expression(
    body=List(
        elts=[
            Constant(value=1),
            Constant(value=2),
            Constant(value=3)],
        ctx=Load()))
>>> print(ast.dump(ast.parse('(1, 2, 3)', mode='eval'), indent=4))
Expression(
    body=Tuple(
        elts=[
            Constant(value=1),
            Constant(value=2),
            Constant(value=3)],
        ctx=Load()))
class ast.Set(elts)

一個集合。elts 保存表示集合之元素的節點串列。

>>> print(ast.dump(ast.parse('{1, 2, 3}', mode='eval'), indent=4))
Expression(
    body=Set(
        elts=[
            Constant(value=1),
            Constant(value=2),
            Constant(value=3)]))
class ast.Dict(keys, values)

一個字典 (dictionary)。keysvalues 分別按匹配順序保存表示鍵和值的節點串列(為呼叫 dictionary.keys()dictionary.values() 時將回傳的內容)。

當使用字典文本進行字典解包 (unpack) 時,要擴充的運算式位於 values 串列中,在 keys 中的相應位置有一個 None

>>> print(ast.dump(ast.parse(