我正在尝试编写一个可以帮助我进行调试的函数。我不是偶尔插入几个打印语句,而是要插入单行调试语句。而我期望完成的是这样的功能可以重复使用:我想在几个函数中使用它,以便打印不同的变量集。
特别是我想做的是:
def _debugFunction(var_list):
global debug_flag
if debug_flag:
print 'START DEBUGGING:'
< magic code >
print 'END DEBUGGING'
def foo_function(n):
x = 1
y = 2
z = 'string'
debugFunction([x, y, z])
return x + y
因此,当我设置debug_flag = True
并调用foo时,输出为:
START DEBUGGING:
x = 1
y = 2
z = 'string'
END DEBUGGING
3
然后,如果我设置debug_flag = False
并调用foo,则输出为:
3
为了做到这一点,我需要在运行时获取作为参数传递给debugFunction()
的变量的名称,并将其与它们的值一起打印。
我搜索了其他帖子,无法找到直接答案。
How to get a variable name as a string in Python?
retrieving a variable's name in python at runtime?
据我所知,人们被告知要使用日志模块。我已经看过它,试图尽可能多地实现它,但我还没有记录任何东西(不会投降)。
我还读到人们会指向__dict()__
,vars()
或dir()
,但却无法理解如何使用它们。而且:如果我尝试将它们应用到我的变量中,我只会得到错误,或者对我的输出没有意义(
有办法做到这一点吗?这是太糟糕的做法吗?如果是这样的话:什么是好的做法?
PS:也许没有必要浪费努力来实现这一点,而是花时间了解如何正确使用日志记录模块。答案 0 :(得分:3)
我还读过人们指向 dict(),vars()或dir(),但却无法理解如何使用它们。
您是否尝试过查看这些功能的文档?例如,vars
:
...没有参数,vars()就像locals()一样。请注意,locals词典仅对读取有用,因为忽略了对locals词典的更新。
好的,那么locals
做了什么?查看文档:它为您提供了一个字典,可以将函数中的每个本地名称映射到其值。因此,如果您不想传递名称和值,请传递locals()
字典和名称:
def foo_function(n):
x = 1
y = 2
z = 'string'
debugFunction(locals(), ['x', 'y', 'z'])
return x + y
def _debugFunction(localdict, var_list):
global debug_flag
if debug_flag:
print 'START DEBUGGING:'
for name in var_list:
print('{}: {}'.format(name, localdict[name]))
print 'END DEBUGGING'
就是这样。除了我可能会稍微更改接口以使用*var_list
,或者更简单的是,我可以使用split
的字符串,并且为了在简单的情况下使用更简单,默认为打印所有本地人:
def _debugFunction(localdict, var_list=''):
var_list = var_list.split()
if not var_list:
var_list = localdict.keys()
现在你可以这样做:
_debugFunction(locals(), 'x y z')
或者只是:
_debugFunction(locals())
答案 1 :(得分:0)
你想要的只是几乎可能,仅适用于CPython ......但这是一个非常非常糟糕的主意。这是代码:
def _debugFunction(values):
caller = sys._getframe(1)
revlocals = {value: key for key, value in caller.f_locals.items()}
for value in values:
print('{}: {}'.format(revlocals[value], value))
这里的第一个问题是它只处理本地,而不是全局。这很容易解决,例如,ChainMap
或dict.update
。
第二个问题是,如果你传递一个不绑定任何名字的值(例如,_debugFunction([2])
,它会引发异常。你可以用例如,revlocals.get(value, '<constant>')
。
第三个问题是,如果你传递一个绑定到两个或多个名字的值,它将任意选择一个。因此,此代码将打印&#39; x:0&#39;两次:
x = y = 0
_debugFunction([x, y])
您甚至可以通过查看框架f_code
和f_lasti
来找到它,以便在调用函数时找到它正在执行的字节码,从中您可以知道每个参数的位置来自 - 例如,如果第一个参数来自LOAD_FAST 1
,则名称为f.f_code.co_varnames[1]
。 (或者,您可以将其反编译为AST,这可能更容易处理,但这需要第三方代码。)这甚至更黑 - 它依赖于CPython编译器从不优化传递函数调用的参数的事实任何方式 - 几乎总是如此,虽然我认为有两个边缘情况,它不在Python 3.4中。我会留下代码,发现并解决这些边缘情况,作为读者的练习。
当然转过来并传递名称并使用框架hack只是为了获得与这些名称相关联的值要简单得多。仍然是一个坏主意,仍然很hacky,但没有那么多,我认为我应该拒绝显示代码:
def _debugFunction(names):
f = sys._getframe(1)
values = collections.ChainMap(f.f_locals, f.f_globals)
for name in names.split():
print('{}: {}'.format(name, values.get(name, '<undefined>')))
a = 0
def test(b):
b = c = 1
d = 'abc'
_debugFunction('a b c d e')