我正在调试方法f()
,其中没有return
。
class A(object):
def __init__(self):
self.X = []
def f(self):
for i in range(10):
self.X.append(i)
我需要看看这个方法在调用后如何修改变量X
。为此,我在方法的末尾插入return
,并在那里设置断点:
这样,只要方法到达return
,我就可以看到变量X
的值。
这可以胜任,但我确信有更好的方法。每次我需要调试它时编辑一个方法或函数似乎很愚蠢。
问题:
是否有不同的方法(例如调试器中的选项)在没有return
的方法结束时设置断点?
(请注意,在鼠标悬停时,在函数调用和使用 Step Over 时设置断点不会显示X
,因为该函数是从其他模块调用的。)
答案 0 :(得分:5)
您可以在最后一行添加条件断点,并将条件设置为仅在最后一次迭代中发生的条件。
在这种情况下,条件非常简单,因为它只是i == 9
,但根据你的循环条件,它可能会复杂得多,所以有时在最后添加一个语句将是更容易的解决方案
该屏幕截图来自IntelliJ IDEA,您的屏幕截图看起来就像来自同一个IDE,所以只需右键单击断点即可显示对话框并输入您的条件。
如果您正在使用其他IDE,我确信有能力使断点成为条件。
<强>更新强>
只有在方法开头时才支持在Python debugger中的方法结尾处中断:
b(reak)[[filename:] lineno |功能[,条件]]
使用lineno参数,在当前文件中设置一个中断。使用函数参数,在该函数中的第一个可执行语句处设置break。行号可以以文件名和冒号为前缀,以在另一个文件中指定断点(可能还有一个尚未加载的断点)。在sys.path上搜索该文件。请注意,为每个断点分配一个所有其他断点命令所引用的数字。
如果存在第二个参数,则它是一个表达式,在断点被接受之前必须求值为true。
不带参数,列出所有中断,包括每个断点,断点被击中的次数,当前忽略计数以及相关条件(如果有)。
答案 1 :(得分:3)
有一种快速而肮脏的解决方案适用于支持monkeypatching的任何语言(Python,Ruby,ObjC等)。老实说,我不记得曾经在Python中需要它,但我在SmallTalk和ObjC都做了很多,所以也许它对你有用。
只需在函数中动态包装A.f
,如下所示:
real_A_f = A.f
def wrap_A_f(self, *args, **kwargs):
result = real_A_f(self, *args, **kwargs)
return result
A.f = wrap_A_f
在大多数可编写脚本的调试器中,您应该能够编写一个脚本,按名称自动为方法执行此操作。在pdb中,它允许您在调试器中执行正常的Python代码,它特别简单。
现在你可以在return result
上设置一个断点,并确保在真正的A.f
返回后立即点击(即使它在中间返回或在没有return
声明。
您可能想要添加的一些内容:
A.f
加注,请在代码周围添加try:
和except: raise
,并在raise
上添加断点。A
实例上使用断点,则可以使用检查self is a
的条件断点,或使用types.MethodType
创建绑定实例并将其存储为a.f
。functools.wraps
。wrap_method
函数来执行此操作,然后在提示符处写入p utils.wrap_method(A, 'f')
。但是如果以这种方式包装多个方法,它们将共享相同的断点(在wrap_method
内定义的包装函数内)。在这里,我认为条件断点是唯一合理的选择。exec(real_A_f.__code__, real_A_f.globals())
,但我并不满意。答案 2 :(得分:3)
您的IDE隐藏了引擎盖下的内容。 就是这样的事情
import pdb
前置于您的脚本和
pdb.set_trace()
在您放置断点的行之前插入。
从你说的我推断PyCharm不喜欢在空行上放置断点。但是pdb.set_trace()
可以完美地放在方法的末尾。
所以你可以自己插入(或写一个宏)并运行python -m pdb
来开始调试。
(编辑)示例
import pdb
class A(object):
def __init__(self):
self.X = []
def f(self):
for i in range(10):
self.X.append(i)
pdb.set_trace()
if __name__ == '__main__':
a = A()
a.f()
使用
进行调试$ python -m pdb test.py
> /dev/test.py(1)<module>()
----> 1 import pdb
2
3 class A(object):
ipdb> cont
--Return--
> /dev/test.py(11)f()->None
-> pdb.set_trace()
(Pdb) self.X
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
(Pdb)
可以使用 ipdb
代替pdb
。
答案 3 :(得分:2)
使用pdb
,您可以使用break function
和until lineno
的完美组合:
不带参数,继续执行直到带数字的行 达到目前的水平。
使用行号,继续执行,直到带有数字的行 达到或大于等于。在这两种情况下,也都停止了 当前帧返回。
在版本3.2中更改:允许给出明确的行号。
你可以实现你所需要的。
我稍微修改了你的例子(所以你会看到该指令被执行虽然pdb将其报告为“ next instruction ”):
01: class A(object):
02:
03: def __init__(self):
04: self.X = []
05:
06: def f(self):
07: print('pre exec')
08: for i in range(10):
09: self.X.append(i)
10: print('post exec')
11:
12: a = A()
13: a.f()
14: print('Game is over')
15:
使用python -m pdb test.py
运行的结果如下:
开始调试并在类声明后运行它(因此可以添加命名断点):
> d:\tmp\stack\test.py(1)<module>()
-> class A(object):
(Pdb) until 11
> d:\tmp\stack\test.py(12)<module>()
-> a = A()
现在,在函数的开头打破:
(Pdb) break A.f
Breakpoint 1 at d:\tmp\stack\test.py:6
继续执行直到遇到断点:
(Pdb) continue
> d:\tmp\stack\test.py(7)f()
-> print('pre exec')
利用“当当前帧返回时停止:
(Pdb) until 14
pre exec
post exec
--Return--
正如您所看到的,打印了 pre exec 和 post exec ,但在执行where
时,您仍然在f()
:< / p>
(Pdb) w
c:\python32\lib\bdb.py(405)run()
-> exec(cmd, globals, locals)
<string>(1)<module>()
d:\tmp\stack\test.py(13)<module>()
-> a.f()
> d:\tmp\stack\test.py(10)f()->None
-> print('post exec')
> d:\tmp\stack\test.py(10)f()->None
-> print('post exec')
并且所有上下文变量都是完整的:
(Pdb) p self.X
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
现在以你的真实生活为例:
01: class A(object):
02: def __init__(self):
03: self.X = []
04:
05: def f(self):
06: for i in range(10):
07: self.X.append(i)
08:
09: a = A()
10: a.f()
11: print('Game is over')
以与以前类似的方式开始:
> d:\tmp\stack\test.py(1)<module>()
-> class A(object):
(Pdb) until 8
> d:\tmp\stack\test.py(9)<module>()
-> a = A()
(Pdb) break A.f
Breakpoint 1 at d:\tmp\stack\test.py:5
(Pdb) cont
> d:\tmp\stack\test.py(6)f()
-> for i in range(10):
现在...... f.A
中的断点实际上意味着在f.A
的第一个语句处断点,不幸的是for i in...
因此每次都会断开它。
如果您实际上没有使用循环启动实际代码,则可以跳过此部分。
(Pdb) disable 1
Disabled breakpoint 1 at d:\tmp\stack\test.py:5
再次使用until <end of file>
:
(Pdb) until 10
--Return--
> d:\tmp\stack\test.py(6)f()->None
-> for i in range(10):
同样,所有帧变量都可用:
(Pdb) p i
9
(Pdb) w
c:\python32\lib\bdb.py(405)run()
-> exec(cmd, globals, locals)
<string>(1)<module>()
d:\tmp\stack\test.py(10)<module>()
-> a.f()
> d:\tmp\stack\test.py(6)f()->None
-> for i in range(10):
(Pdb)
这里令人遗憾的是,我想尝试这种自动化:
(Pdb) break A.f
Breakpoint 1 at d:\tmp\stack\test.py:5
(Pdb) commands 1
(com) disable 1
(com) until 11
(com) end
哪个会自动执行您需要的所有内容(当您至少有一个预循环语句时,再次,disable 1
不需要),但根据commands
上的文档:
指定任何恢复执行的命令(当前继续,步骤,下一步,返回,跳转,退出及其缩写)将终止命令列表(就好像该命令紧跟在结束之后)。这是因为每当你恢复执行时(即使使用简单的下一步或步骤),你可能会遇到另一个断点 - 它可能有自己的命令列表,导致对要执行的列表的含糊不清。
所以until
似乎不起作用(至少对于Windows下的Python 3.2.5),你必须手动完成。
答案 4 :(得分:0)
为什么不把回程留在那里?或return None
。无论如何,它是隐含的,无论如何,解释器/编译器都会做同样的事情:
事实上,即使是没有return语句的函数也会返回一个值,尽管它是一个相当无聊的值。该值称为None(它是内置名称)。