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

astunparse模块报错:TypeError: 无法用NoneType非整数乘序列

问题分析与解决方案

错误根源

1. 非法的输入代码

你的original_code是一段错误追踪文本,并非合法的Python类代码。ast.parse解析这种非法内容时,会生成结构异常的AST节点,其中可能包含ImportFrom节点但level属性为None(正常情况下level应为整数,0代表绝对导入),这直接导致astunparse在处理时触发乘法错误。

2. AST结构被破坏

created_functions和created_dicts列表中混合了字符串(如函数名、字典名)与合法AST节点。tree.body只能包含ast.stmt类型的节点,混入字符串会彻底破坏AST结构,引发后续解析错误。

3. 遍历AST的方式不安全

使用ast.walk遍历AST时直接修改tree.body,会因为列表长度变化导致索引偏移,可能出现节点处理遗漏或错误。

修正后的代码

import ast
import astunparse

def transform_code(source_code):
    tree = ast.parse(source_code)

    # 仅保存创建的AST节点,无需保存名称字符串
    created_functions = []
    created_dicts = []

    def visit_class(node):
        nonlocal created_functions, created_dicts

        class_name = node.name
        functions = [n for n in node.body if isinstance(n, ast.FunctionDef)]

        # 为类中每个方法生成独立函数
        for func in functions:
            func_name = f"{class_name}_{func.name}"
            func_def = ast.FunctionDef(
                name=func_name,
                args=func.args,
                body=func.body,
                decorator_list=func.decorator_list  # 保留原装饰器
            )
            ast.fix_missing_locations(func_def)
            created_functions.append(func_def)

        # 生成类属性对应的字典
        dict_name = f"{class_name}_dict"
        dict_assign = ast.Assign(
            targets=[ast.Name(id=dict_name, ctx=ast.Store())],
            value=ast.Call(
                func=ast.Name(id="create_material", ctx=ast.Load()),
                args=[],
                keywords=[],
            ),
        )
        ast.fix_missing_locations(dict_assign)
        created_dicts.append(dict_assign)

        # 生成将方法绑定到字典的代码
        update_call = ast.Expr(
            value=ast.Call(
                func=ast.Attribute(
                    value=ast.Name(id=dict_name, ctx=ast.Load()),
                    attr="update",
                    ctx=ast.Load(),
                ),
                args=[],
                keywords=[
                    ast.keyword(
                        arg=func.name,
                        value=ast.Name(id=f"{class_name}_{func.name}", ctx=ast.Load()),
                    )
                    for func in functions
                ],
            )
        )
        ast.fix_missing_locations(update_call)
        return [update_call]

    # 先收集所有ClassDef节点的索引,反向遍历避免索引偏移
    class_nodes = [(idx, n) for idx, n in enumerate(tree.body) if isinstance(n, ast.ClassDef)]
    for idx, node in reversed(class_nodes):
        tree.body[idx:idx+1] = visit_class(node)

    # 添加所需导入
    imports = [
        ast.Import(names=[ast.alias(name="load_image", asname=None)]),
        ast.Import(names=[ast.alias(name="vec3", asname=None)]),
    ]

    # 重组AST body:导入 -> 生成的函数 -> 生成的字典 -> 原代码(替换类后的内容)
    tree.body = imports + created_functions + created_dicts + tree.body

    # 转换回源代码
    transformed_code = astunparse.unparse(tree)
    return transformed_code

# 合法的测试类代码
original_code = """
class Material:
    def __init__(self, color):
        self.color = color
    
    def render(self):
        print(f"Rendering material with color {self.color}")
"""

transformed_code = transform_code(original_code)
print(transformed_code)

关键修正点

  • 将original_code替换为合法的Python类代码,确保ast.parse生成正常的AST结构。
  • 移除created_functions和created_dicts中的字符串元素,仅保留AST节点。
  • 改用先收集节点索引再反向遍历的方式处理ClassDef节点,避免索引偏移问题。
  • 为所有新创建的AST节点(包括嵌套节点)调用ast.fix_missing_locations,确保位置信息完整。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 22:55:16