我正在尝试获取Python中生成器执行的最后一行数。在几种情况下我找到了解决方法:
yield
暂停,StopIteration
),StopIteration
。但是我要求提供关于如何在生成器执行StopIteration
语句之后自动引发return
或者只是到达相应函数的最后一行时获取行号的建议。
我使用的简化代码示例如下所示。
from sys import exc_info
def gen1():
print("1.1")
yield
print("1.2")
raise StopIteration()
def gen2():
print("2.1")
yield
print("2.2")
return
def gen3():
print("3.1")
yield
print("3.2")
raise RuntimeError()
for gen in [ g for n, g in globals().items() if n.startswith("gen") ]:
g = gen() # launch the generator
while True:
try:
next(g)
except StopIteration: # generator normally returned
tb = exc_info()[2].tb_next
if tb is None:
print("How to get last line?")
else:
print("last line: %s" % tb.tb_frame.f_lineno)
break
except BaseException: # generator failure
tb = exc_info()[2].tb_next
print("last line: %s" % tb.tb_frame.f_lineno)
break
else:
print("last line: %s" % g.gi_frame.f_lineno)
执行此代码返回:
3.1
last line: 17
3.2
last line: 19
2.1
last line: 11
2.2
How to get last line?
1.1
last line: 5
1.2
last line: 7
答案 0 :(得分:2)
您想要的信息位于框架对象中。
但框架对象已被垃圾收集。当函数返回时,g.gi_frame
中对它的引用设置为None
。
回溯不会对你有所帮助,因为追溯功能来自解释器在函数返回后的机器,所以它无处可去(而且你已经看到它没有&#39甚至尝试)。
但是如果您可以更改代码以在生成器运行时获取对gi_frame
对象的引用并在完成后保留它,那么您将保留该代码以及所有其他相关的垃圾-alive。
这意味着f_lineno
可能有效,正如您在上一个案例中所使用的那样,从未发生过。我不知道是否有任何方法可以检查何时f_lineno
并且没有意义,但产生回溯的代码可能知道如果您深入了解其来源。
如果不是,f_lasti
肯定是RETURN_VALUE
op的字节码偏移量。在发布框架之前,Python当然不能保证它与lasti
没有任何关系,但它在任何版本中都没有,而且它还没有。很难看出将来会有什么理由这样做。因此,使用f_code.co_lnotab
中的信息生成f_lasti
的行号始终是正确的。请参阅dis
模块以获取相关帮助。 (如果你的Python足够新,它会公开一个为你做的findlinestarts
;如果没有,请阅读模块的源代码。或者从Objects
目录中的文档中编写自己的lnotab解析器。翻译来源,如果你想要一些真正的乐趣。)