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

Python生成器自动触发StopIteration时,如何获取最后执行行号

获取生成器正常结束时的最后执行行号

这确实是个挺细节的需求,我之前也遇到过类似的场景。当生成器通过return或者执行到函数末尾自动触发StopIteration时,我们可以借助Python的inspect模块和生成器的帧属性来搞定,下面给你两种可行的方案:

方案一:用跟踪器包装生成器,实时记录帧行号

生成器对象自带gi_frame属性,指向它当前的执行帧。我们可以写一个包装器,在迭代生成器的过程中持续记录帧的行号,直到捕获StopIteration时,拿到最后一次执行的行号:

import inspect

def track_gen_last_lineno(gen):
    last_lineno = None
    try:
        while True:
            # 每次迭代前先更新当前帧的行号
            frame = gen.gi_frame
            if frame:
                last_lineno = frame.f_lineno
            yield next(gen)
    except StopIteration:
        # 生成器结束前,最后一次获取帧行号(对应return或函数末尾行)
        frame = gen.gi_frame
        if frame:
            last_lineno = frame.f_lineno
        # 将行号作为StopIteration的返回值抛出
        return last_lineno

# 测试生成器1:带return语句
def test_gen_with_return():
    yield "first"
    yield "second"
    return "finished"

# 测试生成器2:无return,执行到函数末尾
def test_gen_no_return():
    yield "a"
    yield "b"
    print("end")

# 使用示例:跟踪带return的生成器
gen1 = test_gen_with_return()
tracker1 = track_gen_last_lineno(gen1)
try:
    while True:
        next(tracker1)
except StopIteration as e:
    print(f"带return的生成器最后执行行号: {e.value}")

# 使用示例:跟踪无return的生成器
gen2 = test_gen_no_return()
tracker2 = track_gen_last_lineno(gen2)
try:
    while True:
        next(tracker2)
except StopIteration as e:
    print(f"无return的生成器最后执行行号: {e.value}")

原理说明

  • 每次迭代生成器时,我们都会检查它的gi_frame,记录当前执行的行号。
  • 当生成器执行完return或者函数最后一行代码后,会自动抛出StopIteration,此时我们最后一次读取gi_frame的f_lineno,就能拿到结束前执行的最后一行的行号。
  • 注意:如果生成器结束后过了很久才去读取gi_frame,可能帧已经被Python的垃圾回收机制销毁,导致无法获取行号,所以建议在捕获StopIteration的同时立即获取。

方案二:自定义跟踪型生成器类(手动控制)

如果觉得依赖帧属性不够稳妥,也可以自定义一个类来模拟生成器,手动在关键位置记录行号。这种方法虽然需要修改生成器的写法,但可控性更强:

import inspect

class TrackedGen:
    def __init__(self):
        self.last_lineno = None

    def __iter__(self):
        self.last_lineno = inspect.currentframe().f_lineno
        yield "step1"
        self.last_lineno = inspect.currentframe().f_lineno
        yield "step2"
        self.last_lineno = inspect.currentframe().f_lineno
        return "done"

# 使用示例
gen = TrackedGen()
try:
    for _ in gen:
        pass
except StopIteration:
    print(f"自定义生成器最后执行行号: {gen.last_lineno}")

适用场景

这种方法适合逻辑简单、行数较少的生成器,你可以精确控制在哪些行记录行号,避免依赖生成器的内部帧属性。

额外说明:处理嵌套生成器(yield from)

如果你的生成器里用到了yield from调用其他生成器,需要递归获取内层生成器的gi_yieldfrom属性,才能拿到真正执行的帧:

def get_inner_frame(gen):
    current_gen = gen
    while current_gen.gi_yieldfrom:
        current_gen = current_gen.gi_yieldfrom
    return current_gen.gi_frame

# 在跟踪器里替换获取帧的逻辑:frame = get_inner_frame(gen)

内容的提问来源于stack exchange,提问作者Vasily E.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:51:51