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

Python 3上下文起止行号获取问题:Python2.7方法在3.7失效

解决Python 3.7中无法正确获取上下文起止行号的问题

你在Python 2.7里写的那段上下文管理器代码确实很巧妙,靠sys._getframe(2)拿到外层调用帧的行号,能精准捕获with块的起始和结束行。但到了Python 3.7就掉链子了,结束行号只拿到了14而不是预期的15——这其实是Python 3对帧对象的行号更新逻辑做了调整导致的。

问题根源

在Python 2.7中,当yield执行完毕回到上下文管理器时,帧对象的f_lineno会指向with块的最后一行代码。但Python 3.7及以后版本里,这个行号会停在with块中最后一条执行语句的起始行,对于跨多行的表达式(比如你写的(200, 300)),它只会记录表达式开始的行号,而不是结束行。

修复方案

要准确获取with块的起止行,我们可以换个思路:通过**抽象语法树(AST)**解析代码,找到with语句的完整范围。下面是调整后的代码:

from contextlib import contextmanager
import sys
import inspect
import ast

@contextmanager
def print_start_end_ctx():
    # 获取调用者的帧,拿到with语句的起始行号
    caller_frame = sys._getframe(2)
    start_line = caller_frame.f_lineno
    
    # 获取调用者的源码并解析成AST
    source = inspect.getsource(caller_frame)
    tree = ast.parse(source)
    
    # 遍历语法树,找到对应with语句的结束行号
    end_line = start_line
    for node in ast.walk(tree):
        if isinstance(node, ast.With) and node.lineno == start_line:
            end_line = node.end_lineno
            break
    
    yield
    
    print(f"start_line={start_line}\nend_line={end_line}")

# 测试代码
with print_start_end_ctx():
    100
    (200,
    300)

代码说明

  1. 进入上下文时,还是通过sys._getframe(2)拿到with语句的起始行号。
  2. 用inspect.getsource获取调用者的源码,再通过ast.parse解析成语法树,AST会完整保留代码的结构信息。
  3. 遍历语法树找到对应行号的With节点,它的end_lineno属性就是with块的结束行号。
  4. 退出上下文时,直接用之前找到的end_line输出即可。

这段代码在Python 3.7+环境下运行,会正确输出:

start_line=22
end_line=25

(注:实际行号会根据你代码的位置变化,核心是能精准捕获with块的最后一行)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 05:26:20