我正在调试一个python脚本,我想看一个变量进行更改(很像你可以在gdb中观看内存地址)。有没有办法做到这一点?
答案 0 :(得分:23)
使用pdb
执行此操作非常简单。每次使用~/.pdbrc
时,都可以将这些命令放入pdb
进行自动加载。
!global __currentframe, __stack; from inspect import currentframe as __currentframe, stack as __stack
!global __copy; from copy import copy as __copy
!global __Pdb; from pdb import Pdb as __Pdb
!global __pdb; __pdb = [__framerec[0].f_locals.get("pdb") or __framerec[0].f_locals.get("self") for __framerec in __stack() if (__framerec[0].f_locals.get("pdb") or __framerec[0].f_locals.get("self")).__class__ == __Pdb][-1]
alias _setup_watchpoint !global __key, __dict, __val; __key = '%1'; __dict = __currentframe().f_locals if __currentframe().f_locals.has_key(__key) else __currentframe().f_globals; __val = __copy(%1)
alias _nextwatch_internal next;; !if __dict[__key] == __val: __pdb.cmdqueue.append("_nextwatch_internal %1")
alias _stepwatch_internal step;; !if __dict[__key] == __val: __pdb.cmdqueue.append("_stepwatch_internal %1")
alias nextwatch __pdb.cmdqueue.extend(["_setup_watchpoint %1", "_nextwatch_internal"])
alias stepwatch __pdb.cmdqueue.extend(["_setup_watchpoint %1", "_stepwatch_internal"])
这会添加两个命令nextwatch
和stepwatch
,每个命令都使用变量名 varname 作为参数。如果可能,他们将为 varname 创建当前帧的局部变量的浅表副本,并分别继续执行next
或step
,直到该名称指向更改为止。
这适用于CPython 2.7.2,但依赖于某些pdb
内部,因此它可能会在其他地方发生。
答案 1 :(得分:21)
要在遇到断点时观察变量,可以使用commands
命令。例如。在遇到断点#1(canonical example from pdb
doc)时打印some_variable
。
(Pdb) commands 1
(com) print some_variable
(com) end
(Pdb)
Python 3的更新
(Pdb) commands 1
(com) print(some_variable)
(com) end
(Pdb)
此外,您可以使用condition
命令确保仅在变量获取特定值时才会触发断点。
例如:
(Pdb) condition 1 some_variable==some_value
答案 2 :(得分:15)
Python 3 :
您可以使用pdb的 display 功能
点击断点后,只需输入
即可IPDB> 显示 表达
示例:强>
ipdb> display instance
display instance: <AppUser: dmitry4>
ipdb> display instance.id
display instance.id: 9
ipdb> display instance.university
display instance.university: <University: @domain.com>
ipdb> display
Currently displaying:
instance.university: <University: @domain.com>
instance.id: 9
instance: <AppUser: dmitry4>
ipdb>
正如您所看到的,每次键入显示时 - 它都会打印所有手表(表达式)。您可以使用内置函数undisplay
删除某些监视。
您还可以使用 pp表达式来打印表达式(非常有用)
答案 3 :(得分:12)
可能的解决方案是使用pdb++:
pip install pdbpp
然后使用装饰器@pdb.break_on_setattr
“标记”您要观看的对象:
from pdb import break_on_setattr
@break_on_setattr('bar')
class Foo(object):
pass
f = Foo()
f.bar = 42 # the program breaks here
此处pdb
将在任何Foo对象上的属性bar
的任何更改中中断。
<强>注意事项强>
只有对基础__setattr__
- 方法的调用才会触发断点。这意味着f.bar = 'XYZ'
和setattr(f, 'XYZ')
将起作用,但操纵bar
- 对象不会触发断点:
f.bar = []
f.bar.append(7) # will NOT trigger breakpoint
f.bar = 2
f.bar += 5 # will trigger breakpoint
注意:@break_on_setattr
不是标准pdb
- 模块的一部分。 pdb
- 包覆盖pdbpp
pdb.set_trace()
。
您还可以在(Pdb++) import pdb
(Pdb++) pdb.break_on_setattr('tree_id')(self.__class__)
(Pdb++) continue
:
var signatureHashHexExpected = "559bd871bfd21ab76ad44513ed5d65774f9954d3232ab68dab1806163f806447";
var signature = "123456:some-string:2016-04-12T12:44:16Z";
var key = "AgQGCAoMDhASFAIEBggKDA4QEhQCBAYICgwOEBIUAgQ=";
var shaKeyBytes = Convert.FromBase64String(key);
using (var shaAlgorithm = new System.Security.Cryptography.HMACSHA256(shaKeyBytes))
{
var signatureBytes = System.Text.Encoding.UTF8.GetBytes(signature);
var signatureHashBytes = shaAlgorithm.ComputeHash(signatureBytes);
var signatureHashHex = string.Concat(Array.ConvertAll(signatureHashBytes, b => b.ToString("X2"))).ToLower();
System.Diagnostics.Debug.Assert(signatureHashHex == signatureHashHexExpected);
}
答案 4 :(得分:2)
这是我的ipdb和python 3.7采用的Michael Hoffman's解决方案版本(与检查类类型和dict的has_key有关的更改):
!global __currentframe, __stack; from inspect import currentframe as __currentframe, stack as __stack
!global __copy; from copy import copy as __copy
!global __Pdb; from IPython.terminal.debugger import TerminalPdb as __Pdb
!global __pdb_list; __pdb_list = [__fr[0].f_locals.get("pdb") or __fr[0].f_locals.get("self") for __fr in __stack() if ((type(__fr[0].f_locals.get("pdb")) is __Pdb) or (type(__fr[0].f_locals.get("self")) is __Pdb))]
!global __pdb; __pdb = __pdb_list[0]
alias _setup_watchpoint !global __key, __dict, __val; __key = '%1'; __dict = __currentframe().f_locals if (__key in __currentframe().f_locals) else __currentframe().f_globals; __val = __copy(%1)
alias _nextwatch_internal next;; !if __dict[__key] == __val: __pdb.cmdqueue.append("_nextwatch_internal %1")
alias _stepwatch_internal step;; !if __dict[__key] == __val: __pdb.cmdqueue.append("_stepwatch_internal %1")
alias nextwatch __pdb.cmdqueue.extend(["_setup_watchpoint %1", "_nextwatch_internal"])
alias stepwatch __pdb.cmdqueue.extend(["_setup_watchpoint %1", "_stepwatch_internal"])
要在python 3.7和pdb中使用它,请更改__Pdb的定义,如下所示:
!global __Pdb; from pdb import Pdb as __Pdb
答案 5 :(得分:0)
两个现有的答案都有一些本质的麻烦。
直接在pdb中执行此操作,或使用一些技巧来实现观察点,仅在您处于同一范围内时才可以使用。当您脱离框架时,它将无法正常工作。
def change(var):
var.append(1)
a = []
change(a)
它无法捕获函数中发生的事情,仅在change(a)
之后知道变量a已更改。当功能不重要时,此方法有效,但实际上,功能可能很深,以至于有价值的东西都在里面。否则可能会更糟,a
可能会返回并传递,并且永远不会更改当前范围。然后,您将永远不会捉住它。
只有您可以创建自己的类,__setattr__
方法才有效。它不能处理我上面提到的简单情况。在许多情况下,使用新型对象是不可接受的。例如,如果您使用内置的dict
或list
。
我建议使用一个名为watchpoints的库。
使用它比pdb容易得多(如果您还不熟悉它的话),并且比该线程中的任何一种解决方案都涵盖更多的情况。
您唯一需要做的就是watch
您要观看的变量。
from watchpoints import watch
a = []
watch(a)
a.append(1) # will print the changes
输出应为
> <module> (my_script.py:5):
> a = 1
a:
0
->
1
它适用于内置类型(包括不可变类型),自定义类,甚至属性和索引。例如,您可以执行watch(my_obj.my_attr)
,它就可以正常工作。
您提到了pdb
,因此除了简单地知道变量已更改之外,您还可能需要实现其他目标。 watchpoints
工作时,pdb
支持breakpoints()
上拉。
您需要配置watchpoints
,
watch.config(pdb=True)
然后再添加watch
项。更改变量后将弹出pdb
。您可以q(uit)
pdb
,下次更改变量时,它将显示它。
您可以参考github页以更多地使用该库。