You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python3.8环境下如何为ast.AST内置类动态添加属性方法?

兼容Python 3.8的ast.AST扩展可行方案

以下两种方案均可实现需求,不需要修改内置类,无兼容性问题:

方案1:代理包装类(优先推荐)

该方案完全规避内置类修改限制,兼容全Python版本,无副作用:

import ast
from functools import cached_property
from typing import Any

class ExtendedAST:
    def __init__(self, raw_node: ast.AST, parent: "ExtendedAST" = None):
        self._raw_node = raw_node
        self._parent = parent
        # 递归包装所有子节点
        for field, value in ast.iter_fields(raw_node):
            if isinstance(value, ast.AST):
                setattr(self, field, ExtendedAST(value, self))
            elif isinstance(value, list) and all(isinstance(item, ast.AST) for item in value):
                setattr(self, field, [ExtendedAST(item, self) for item in value])
            else:
                setattr(self, field, value)

    # 扩展属性:父节点
    @cached_property
    def parent(self):
        return self._parent

    # 扩展属性:直接子节点
    @cached_property
    def children(self):
        child_list = []
        for attr_value in self.__dict__.values():
            if isinstance(attr_value, ExtendedAST):
                child_list.append(attr_value)
            elif isinstance(attr_value, list):
                child_list.extend([item for item in attr_value if isinstance(item, ExtendedAST)])
        return child_list

    # 透传原生ast节点的所有属性、方法,保证API兼容
    def __getattr__(self, name: str) -> Any:
        return getattr(self._raw_node, name)

使用方式:将ast.parse返回的根节点传入包装类一次即可,所有子节点会自动完成包装:

raw_root = ast.parse("a = 1 + 2")
wrapped_root = ExtendedAST(raw_root)
# 正常调用原生属性
print(wrapped_root.body[0].targets[0].id) # 输出a
# 调用扩展属性
print(wrapped_root.body[0].parent is wrapped_root) # 输出True

方案2:弱引用字典存储扩展属性

如果不想引入包装层修改原有调用逻辑,可以使用该方案:

import ast
from weakref import WeakKeyDictionary
from functools import wraps, cached_property

# 弱引用字典存储节点扩展属性,不会干扰GC回收
_node_extra_store = WeakKeyDictionary()

def ast_extension(func):
    @wraps(func)
    def wrapper(node, *args, **kwargs):
        if node not in _node_extra_store:
            _node_extra_store[node] = {}
        return func(node, _node_extra_store[node], *args, **kwargs)
    # 注册到ast模块命名空间,全局可调用
    setattr(ast, func.__name__, wrapper)
    return wrapper

定义扩展属性/方法示例:

@ast_extension
def set_parent(node, ext_store, parent):
    ext_store["parent"] = parent

@ast_extension
def get_parent(node, ext_store):
    return ext_store.get("parent")

@ast_extension
@cached_property
def get_children(node, ext_store):
    return [n for n in ast.iter_child_nodes(node)]

使用方式:在首次遍历AST时为所有节点赋值父节点后,即可全局调用扩展方法:

root = ast.parse("a = 1 + 2")
# 首次遍历赋值父节点
for node in ast.walk(root):
    for child in ast.iter_child_nodes(node):
        ast.set_parent(child, node)
# 调用扩展方法
print(ast.get_parent(root.body[0]) is root) # 输出True

内容的提问来源于stack exchange,提问作者pawamoy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 15:39:02