打印'var'= var

时间:2014-07-24 18:38:32

标签: python function debugging

我正在尝试编写一个可以帮助我进行调试的函数。我不是偶尔插入几个打印语句,而是要插入单行调试语句。而我期望完成的是这样的功能可以重复使用:我想在几个函数中使用它,以便打印不同的变量集。

特别是我想做的是:

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:也许没有必要浪费努力来实现这一点,而是花时间了解如何正确使用日志记录模块。

2 个答案:

答案 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))

这里的第一个问题是它只处理本地,而不是全局。这很容易解决,例如,ChainMapdict.update

第二个问题是,如果你传递一个不绑定任何名字的值(例如,_debugFunction([2]),它会引发异常。你可以用例如,revlocals.get(value, '<constant>')

第三个问题是,如果你传递一个绑定到两个或多个名字的值,它将任意选择一个。因此,此代码将打印&#39; x:0&#39;两次:

x = y = 0
_debugFunction([x, y])

您甚至可以通过查看框架f_codef_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')