为什么breakpoint()不在for循环中提示幂等位置?

2024-06-02 09:15:00 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个非常简单的测试文件breakpoint_test.py

for i in range(3):                       #1
    breakpoint()                         #2
    print(f"first print in loop {i}")    #3
    breakpoint()                         #4
    print(f"second print in loop {i}")   #5

这里是我运行它并按住c继续时的输出

(py3) nakita@machine:~/tmp $ python breakpoint_test.py 
> /Users/nakita/tmp/breakpoint_test.py(3)<module>()
-> print(f"first print in loop {i}")                    <---------------looks good here!
(Pdb) c
first print in loop 0
> /Users/nakita/tmp/breakpoint_test.py(5)<module>()
-> print(f"second print in loop {i}")
(Pdb) c
second print in loop 0


> /Users/nakita/tmp/breakpoint_test.py(2)<module>()
-> breakpoint()                                         <---------------weird here!
(Pdb) c
first print in loop 1
> /Users/nakita/tmp/breakpoint_test.py(5)<module>()
-> print(f"second print in circle {i}")
(Pdb) c
second print in loop 1


> /Users/nakita/tmp/breakpoint_test.py(2)<module>()
-> breakpoint()
(Pdb) c
first print in loop 2
> /Users/nakita/tmp/breakpoint_test.py(5)<module>()
-> print(f"second print in circle {i}")
(Pdb) c
second print in loop 2

我希望breakpoint()会提示将在breakpoint()之后立即执行的行。当代码第一次进入for循环时就是这样。但在第二个循环中迭代时,第一个breakpoint()会提示breakpoint()行本身,而不是-> print(f"first print in loop {i}")。然而,循环体中的第二个breakpoint()正如我所期望的那样工作。看起来行为首先是breakpoint(),for循环体将被牺牲。有人知道为什么吗

我在python3.7.3和3.8.0上测试了它。 我读过PEP 553 -- Built-in breakpoint()


Tags: inpytestloopforhereuserstmp
1条回答
网友
1楼 · 发布于 2024-06-02 09:15:00

Python正试图在下一行的开头开始调试,但是它的“下一行”检测有点不可靠


默认情况下,breakpoint调用pdb.set_trace,这将设置将在下一个跟踪事件上执行的trace function。本例中的下一个跟踪事件是'line'事件,当Python认为执行已进入新行时触发

循环中第一个breakpoint的第一次执行和第二个breakpoint的所有执行时,下一个'line'事件在下一行的第一个操作码上触发。但是,在第一个breakpoint的第二次和以后的执行中,会发生一些不同的情况

Python通过检查当前字节码指令索引是对应于行的第一条指令,还是对应于执行的最后一条指令之前的索引处的指令来确定新的源代码行已经开始。你可以在^{}Python/ceval.c中看到

当跟踪处于活动状态时,Python只更新instr_prev,该变量用于确定最后执行的指令。当您点击c继续执行时,trace函数被停用(如果您使用PDB break命令设置了任何断点,它将保持活动状态,因为跟踪函数需要处理这些断点,但是breakpoint()调用不会通过该机制。)

在“非怪异”断点上,Python在“行的第一条指令”条件下,在下一行的第一个操作码上触发下一行事件

在“怪异”断点上,instr_prev仍然具有上次命中c时的值,因为此时已禁用跟踪。该值用于比当前行晚的行,因此Python在“在执行最后一条指令之前的索引处的指令”条件下,在breakpoint()行的下一个操作码上触发next line事件(breakpoint()行的下一个操作码是POP_TOP,用于清除breakpoint的返回值。)如果Python能够更好地跟踪最后执行的指令,它就不会触发该事件

相关问题 更多 >