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

Python中能否通过栈帧获取当前多行语句的完整行范围及源代码

底层设计原因

CPython的栈帧中f_lineno字段仅存储当前正在执行的字节码对应的单一行号,是设计取舍的结果:Python将多行语句/表达式编译为字节码时,仅会为每条字节码关联实际承载执行逻辑的行号,不会额外存储整条语句的起止行范围。该设计可以降低编译阶段的计算开销,减少运行时栈帧的内存占用,而绝大多数开发场景下仅需当前执行行号即可满足需求,因此Python原生没有提供对应接口。

可行绕过方案

不推荐使用正则加括号平衡的实现,这类方案无法处理注释、字符串转义、多层嵌套等边界场景,稳定性差。可选择以下两种更可靠的实现:

  • 基于AST的实现(Python 3.8+)
    Python 3.8及以上版本的AST节点原生携带lineno(语句起始行)、end_lineno(语句结束行)属性,你可以通过解析当前帧对应源码的AST,反向匹配当前行所属的语句节点即可拿到起止行号,示例代码如下:
import ast
import inspect
import sys

def get_stmt_range():
    # 取调用方栈帧
    frame = sys._getframe(1)
    # 获取当前函数/模块的源码内容、源码在文件中的起始行偏移
    src_lines, base_lineno = inspect.getsourcelines(frame.f_code)
    src_content = "".join(src_lines)
    # 当前执行行转换为源码片段内的相对行号
    current_rel_lineno = frame.f_lineno - base_lineno + 1
    # 解析AST遍历匹配
    ast_tree = ast.parse(src_content)
    for node in ast.walk(ast_tree):
        if hasattr(node, "lineno") and hasattr(node, "end_lineno"):
            if node.lineno <= current_rel_lineno <= node.end_lineno:
                # 转换为文件绝对行号
                abs_start = base_lineno + node.lineno - 1
                abs_end = base_lineno + node.end_lineno - 1
                return (abs_start, abs_end)
    return None

# 测试用例
print(  # line 24


    get_stmt_range()  # line 27
)
# 输出结果为 (24, 28),符合预期

该方案仅要求源码可被读取(不适用于无源码的打包二进制、动态执行的字符串代码场景),稳定性远高于手写的括号匹配逻辑。

  • 基于tokenize的兼容实现
    如果你需要兼容3.8以下版本的Python,可以使用标准库的tokenize模块解析源码token,跟踪括号的开闭状态定位语句边界,该模块会自动处理字符串、注释、转义字符等特殊场景,比正则实现的稳定性高很多。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 06:24:03