[英]How to get last line executed by a generator in Python
I am trying to get number of last line that has been executed by a generator in Python. 我试图获取由Python生成器执行的最后一行的编号。 I find a ways to get it in several cases: 在几种情况下,我找到了一种获取方法:
yield
, 发电机因yield
停产, StopIteration
), 生成器因异常(不是StopIteration
)而失败, StopIteration
. 通过显式提高StopIteration
返回的生成器。 But I am asking for an advice about how to get the line number in case of the StopIteration
is raised automatically after the generator executed return
statement or just reached last line of corresponding function. 但是我正在寻求有关在生成器执行return
语句后或刚到达相应函数的最后一行后StopIteration
自动升高的情况下如何获取行号的建议。
A simplified code example I used is listed below. 下面列出了我使用的简化代码示例。
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)
Execution of this code returns: 执行此代码将返回:
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
The information you want is in the frame object. 所需的信息在框架对象中。
But the frame object has already been garbage collected. 但是框架对象已经被垃圾回收了。 The reference to it in g.gi_frame
is set to None
when the function returns. 函数返回时, g.gi_frame
中g.gi_frame
的引用将设置为None
。
The traceback won't help you, because the traceback comes from the machinery inside the interpreter after the function has returned, so it has nowhere to get it (and you've already seen that it doesn't even try). 追溯不会对您有帮助,因为该追溯来自函数返回后的解释器内部的机械,因此它无处可寻(您已经看到它甚至没有尝试过)。
But if you can change your code to grab a reference to the gi_frame
object while the generator is running and hold onto it after it finishes, you'll keep that—and all of the other associated garbage—alive. 但是,如果您可以更改代码以在生成器运行时获取对gi_frame
对象的引用,并在完成后保留该引用,那么您以及所有其他相关的垃圾将保持有效。
Which means f_lineno
may be valid, as used in your last case that never happens. 这意味着f_lineno
可能是有效的,就像您上一次从未发生过的情况一样。 I don't know if there's any way to check when f_lineno
is and isn't meaningful, but the traceback-generating code might know that if you dive into its source. 我不知道是否有任何方法可以检查f_lineno
是有意义的,但是如果生成了回溯代码,您可能会知道。
If not, f_lasti
will definitely be the bytecode offset of the RETURN_VALUE
op. 如果不是,则f_lasti
肯定是RETURN_VALUE
op的字节码偏移量。 Python of course doesn't guarantee anywhere that it doesn't do anything funky with the lasti
before releasing the frame, but it doesn't in any version yet, and it's hard to see what reason there would be to do so in the future. 当然,Python不能保证在发布框架之前它不会对lasti
做任何时髦的事情,但是它并没有在任何版本中进行,而且很难知道将来会有什么原因。 So it should always be correct to use the information in f_code.co_lnotab
to generate the line number for f_lasti
. 因此,它应该永远是正确使用信息f_code.co_lnotab
产生的行号f_lasti
。 See the dis
module for help on that. 请参阅dis
模块以获取帮助。 (If your Python is new enough, it exposes a findlinestarts
that does it for you; if not, read the source of the module. Or write your own lnotab parser from the docs in the Objects
directory of the interpreter source, if you want some real fun.) (如果您的Python足够新,它将公开一个findlinestarts
为您完成;否则,请读取模块的源代码。或者,如果需要的话,可以从解释器源的Objects
目录中的docs中编写自己的lnotab解析器。真有趣。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.