是否有办法以编程方式获取Python函数返回的语句的行号?让我们考虑以下示例:
def foo(i: int) -> str:
if i == 1:
return 'he'
elif i == 2:
return 'ha'
return 'he'
如果输入1或3,函数将返回'he'
。但是,除非我可以逐步执行代码,否则我将不知道实际上执行了哪个return 'he'
。
我的应用程序允许用户从GUI中以Python输入业务规则,并让他们自己测试规则。由于我的应用程序无法单步执行代码(它将执行任务委派给独立的python引擎,并且需要字符串或异常。)我想找到一种方法来获取返回的行号,以便突出显示GUI编辑器上的相应行。
一种肮脏的做法是让函数返回一个行号和字符串的元组,但是理想情况下,我希望使用户免于这样做的麻烦。
答案 0 :(得分:1)
bdb
模块使您可以在返回每一帧之前对其进行检查,因此您应该能够在此时检索foo的最终返回值的f_lineno
。示例:
from bdb import Bdb
class ReturnWatcher(Bdb):
def __init__(self):
self.last_encountered_return_line = None
super().__init__()
def user_return(self, frame, return_value):
self.last_encountered_return_line = frame.f_lineno
def foo(i):
if(i==1):
return 'he'
elif(i==2):
return 'ha'
return 'he'
x = ReturnWatcher()
x.runcall(foo, 1)
print("Last return statement executed on line", x.last_encountered_return_line)
x.runcall(foo, 2)
print("Last return statement executed on line", x.last_encountered_return_line)
x.runcall(foo, 3)
print("Last return statement executed on line", x.last_encountered_return_line)
结果:
Last return statement executed on line 11
Last return statement executed on line 13
Last return statement executed on line 14
答案 1 :(得分:1)
您可以要求Python使用sys.settrace()
来通知您任何回报。这是Python在某些事件发生时将调用的钩子函数,也是典型的调试器和分析器如何钩接到Python中的方式。
每当Python输入新的本地范围(用于函数调用,类主体以及理解和生成器表达式)时,将仅为 call 事件调用在sys.settrace()
中注册的函数。 。然后,您可以返回None
(不跟踪此本地范围),或将用于 line , exception 或的跟踪函数返回在该范围内的事件。在Python 3.7中,您可以在框架对象上设置选项,以进一步控制按作用域跟踪函数所调用的详细程度。您可以禁用每行事件,甚至可以启用每个操作码事件。
您可以像这样使用它来记录 return 事件;我对 call 和 return 事件都使用一种跟踪方法:
import inspect
import sys
class ReturnLines:
def __init__(self):
self.returns = []
self._old_trace = None
def start(self):
self._old_trace = sys.gettrace()
sys.settrace(self.trace)
def stop(self):
sys.settrace(self._old_trace)
def __enter__(self):
self.start()
return self.returns
def __exit__(self, *exc):
self.stop()
def trace(self, frame, event, arg):
filename = None
if frame is not None:
filename = inspect.getsourcefile(frame)
if event == 'call':
if filename == __file__:
# skip ourselves
return
try:
# Python 3.7+: only trace exceptions and returns for this call
frame.f_trace_lines = False
except AttributeError:
pass
return self.trace
elif event == 'return':
self.returns.append((filename, frame.f_lineno, arg))
将其放入单独的模块中,然后像上下文管理器一样使用该对象:
from return_recorder import ReturnLines
with ReturnLines() as return_lines:
# run the code you want to trace
# ...
上下文管理器使您可以访问添加了返回值的列表对象(以(filename, linenumber, returned_object)
元组的形式),因此您可以在上下文管理器中执行代码时访问返回信息:
>>> from return_recorder import ReturnLines
>>> def foo(i: int) -> str:
... if i == 1:
... return 'he'
... elif i == 2:
... return 'ha'
... return 'he'
...
>>> with ReturnLines() as return_lines:
... for i in range(3):
... foo(i)
... print(f'<-- i={i}, returned at line {return_lines[-1][1]}')
...
'he'
<-- i=0, returned at line 6
'he'
<-- i=1, returned at line 3
'ha'
<-- i=2, returned at line 5
>>> for filename, lineno, returned in return_lines:
... print(f'{filename}:{lineno}:{returned!r}')
...
None:6:'he'
None:3:'he'
None:5:'ha'
对于交互式解释器,文件名是None
。
bdb
模块是sys.set_trace()
之上的基础,是凯文答案的基础,但并未在Python 3.7+上禁用行跟踪。作为通用调试器框架,它为每个跟踪事件增加了更高的开销。这意味着您正在检测的代码执行速度较慢。