如何在使用pdb调试Python时打印所有变量的值,而不需要指定每个变量?

86
我正在使用pdb调试我的Python脚本,手册上说我可以使用

pdb

命令在某个时间点打印指定变量的值。但是如果我有很多变量,比如20个变量,并且我想跟踪所有变量的值,该怎么办?我要如何在不手动指定每个变量的情况下打印它们所有的值?以这个脚本为例:
a = 1
b = 2
c = 3

我可以使用pdb来调试它,并像这样使用p a,b,c打印它们:

$ python -m pdb test.py 
> /media/test.py(1)<module>()
-> a = 1
(Pdb) n
> /media/test.py(2)<module>()
-> b = 2
(Pdb) n
> /media/test.py(3)<module>()
(Pdb) n
--Return--
> /media/test.py(3)<module>()->None
-> c = 3
(Pdb) p a, b, c
(1, 2, 3)
(Pdb) 

但是我必须手动指定每个变量。有没有一种方法可以一次打印所有变量,而不需要指定每一个变量?


1
执行 locals()globals() 会有帮助吗?请注意,pdb 能够解释任何 Python 代码,而不仅仅是那些特殊命令... - mike.dld
这是一个子重复问题,已在此处回答:枚举或列出程序中的所有变量 - OJFord
不,那不是我要找的。locals()和globals()会打印出很多我不想要的信息,而且数量庞大,会把一切都搞得一团糟。另一个线程上的“解决方案”也不是我要找的。那并不能解决我的问题。 - renatov
你对解决方案的设想是什么?如果有一个名为show_all_my_variables()的函数可用,你希望它输出什么? - munk
2
与手动键入“p var1,var2,var3 ... varn”完全相同的输出。 - renatov
2个回答

126

pdb是一个功能齐全的Python shell,因此您可以执行任意命令。

locals()globals()将显示范围内所有变量及其值。

如果您对值不感兴趣,可以使用dir()

当您在Python中声明一个变量时,它会根据情况放入局部变量或全局变量中,并且无法区分您定义的变量和出于其他原因在作用域内的变量。

使用dir()时,您感兴趣的变量可能位于该列表的开头或结尾。 如果您想获取键值对

过滤locals()可能如下所示:

>>> x = 10
>>> y = 20
>>> {k: v for k,v in locals().iteritems() if '__' not in k and 'pdb' not in k}
{'y': 20, 'x': 10}

如果你的 locals() 函数非常混乱,那么你需要使用更加强力的工具。你可以将以下函数放置在 Pythonpath 上的模块中,在调试会话期间进行导入:

def debug_nice(locals_dict, keys=[]):
    globals()['types'] = `__import__`('types')
    exclude_keys = ['copyright', 'credits', 'False', 
                    'True', 'None', 'Ellipsis', 'quit']
    exclude_valuetypes = [types.BuiltinFunctionType,
                          types.BuiltinMethodType,
                          types.ModuleType,
                          types.TypeType,
                          types.FunctionType]
    return {k: v for k,v in locals_dict.iteritems() if not
               (k in keys or
                k in exclude_keys or
                type(v) in exclude_valuetypes) and
               k[0] != '_'}

我已经在pastebin上添加了一个示例会话。

这种方法有一些情况是无法满足的。您可能希望扩展它以允许您传递类型。但它应该可以让您过滤掉除您定义的变量之外的大多数东西。

dir()

如果您只想获取最后20个值,以便输出类似于>>> p var1 var2 ... varn,那么最好像这样切片dir(),dir()[-20:],但是您将很难看到变量和值之间的关系。例如:“我是在bar之前还是之后声明foo?”

如果您想查看它们之间的关系,可以尝试类似于这样的代码,假设您的变量在dir()的结尾。如果它们在开头,则可以以不同方式切片。如果您的变量不是连续的,则此方法效果不佳。

>>> zip(dir(), [eval(var) for var in dir()])[-4:]
[('a', 10), ('var', 'var'), ('x', 30), ('y', 50)]

1
执行 locals() 或 globals() 会返回一大堆非人类可读的信息。我是不是漏了什么? - renatov
1
这只是一个包含名称和值的字典。你可以尝试使用pprint使其更易读。你也可以尝试过滤掉带有下划线的名称。 - munk
1
你可以更具体地过滤,但我无法给出通用解决方案,因为locals()中的所有内容都可能是你感兴趣的变量。如果你分享你的locals(),我们可以帮助你为这个用例制定更好的过滤器。 - munk
4
@renatov 使用 locals() 或 globals() 方法会返回一大堆难以阅读的信息,可以使用 pp(pretty print)命令对其进行美化打印。pp locals() 或 pp globals()。 - Gaurav
1
对于Python 3:我不得不删除types.TypeType(不确定是否有替代方案),并将locals_dict.iteritems()更改为locals_dict.items() - Jonathon Reinhart
显示剩余4条评论

5

根据这个包含多种编程语言的列表,我们可以:

a = 1
b = 1
n = 'value'
#dir() == ['__builtins__', '__doc__', '__name__', '__package__', 'a', 'b', 'n']
for var in dir()[4:]:
    value_of_var = eval(var)
    print(value_of_var)

输出:

1
1
'value'

将每个标签进行命名,只需要打印 var + " 等于 " + eval(var)
您的“理想输出”与键入 p a, b, ... , n, ... 的结果完全相同。
vars = []
for var in dir()[4:-1]
    vars.append(var)
print(tuple(vars))

输出结果如下:

(1, 1, 'value')

这个命令会打印很多信息,但这不是我想要的。 - renatov
@usmcs 你还没有声明任何变量! - OJFord
@usmcs 是的,但不是用户声明的。问题是什么-您现在有值了... - OJFord
1
@usmcs 这个问题是要打印所有变量的值。这就是我的代码所做的,也是你的样本输出所示的。事实上,你知道它起作用,因为你已经将其编辑到你自己的答案中了... - OJFord
@usmcs 这仍然是不正确的。它获取变量名称,并使用它们来evaluate变量的值。 "你的解决方案确实回答了OP的问题"那么问题在哪里? - OJFord
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接