打印传递给函数的变量名

5
在某些情况下,我希望像这样打印调试风格的输出:
# module test.py
def f()
  a = 5
  b = 8
  debug(a, b) # line 18

我希望debug函数能够打印出以下信息:
debug info at test.py: 18
function f
a = 5
b = 8

我认为可以使用inspect模块来定位堆栈帧,然后找到相应的行,在该行中查找源代码,从那里获取参数的名称。函数名可以通过上移一个堆栈帧来获得。(参数的值很容易获得:它们直接传递给函数debug。) 我的想法正确吗?有没有可以参考的示例?
4个回答

3
您可以按照以下方式进行操作:

您可以按照以下方式进行操作:

import inspect

def debug(**kwargs):
  st = inspect.stack()[1]
  print '%s:%d %s()' % (st[1], st[2], st[3])
  for k, v in kwargs.items():
    print '%s = %s' % (k, v)

def f():
  a = 5
  b = 8
  debug(a=a, b=b) # line 12

f()

这将打印出以下内容:
test.py:12 f()
a = 5
b = 8

1
这很简单明了!但如果可以避免每个变量名都要重复两次,一次作为关键字参数名称,一次作为关键字参数值,我也不介意增加更多的复杂性。 - max
你可以尝试调用 debug(**locals()) - Karl Knechtel

1
你通常是正确的,不过对于这种任务使用AOP会更容易。基本上,而不是每次用每个变量调用“debug”,你可以仅仅用aspect来装饰代码,在特定事件上执行特定操作,例如在进入函数时打印传递的变量及其名称。
请参考this站点和旧的post了解更多信息。

1

是的,你走在正确的路径上。您可能需要查看inspect.getargspec,它将返回传递给函数的参数,可变参数,关键字和默认值的命名元组。

import inspect

def f():
  a = 5
  b = 8
  debug(a, b)


def debug(a, b):
    print inspect.getargspec(debug)
f()

0

这真的很棘手。让我试着给出一个更完整的答案,重用this code,以及Senthil的答案中关于getargspec的提示,这些都在某种程度上激发了我。顺便说一句,在Python 3.0中,getargspec已被弃用,应该使用getfullarcspec代替

这对我来说在Python 3.1.2上都可以工作,无论是显式调用debug函数还是使用装饰器:

# from: https://dev59.com/hW855IYBdhLWcg3wKxHc#4493322
def getfunc(func=None, uplevel=0):
    """Return tuple of information about a function

    Go's up in the call stack to uplevel+1 and returns information
    about the function found.

    The tuple contains
      name of function, function object, it's frame object,
      filename and line number"""
    from inspect import currentframe, getouterframes, getframeinfo
    #for (level, frame) in enumerate(getouterframes(currentframe())):
    #    print(str(level) + ' frame: ' + str(frame))
    caller = getouterframes(currentframe())[1+uplevel]
    # caller is tuple of:
    #  frame object, filename, line number, function
    #  name, a list of lines of context, and index within the context
    func_name = caller[3]
    frame = caller[0]
    from pprint import pprint
    if func:
        func_name = func.__name__
    else:
        func = frame.f_locals.get(func_name, frame.f_globals.get(func_name))
    return (func_name, func, frame, caller[1], caller[2])


def debug_prt_func_args(f=None):
    """Print function name and argument with their values"""
    from inspect import getargvalues, getfullargspec
    (func_name, func, frame, file, line) = getfunc(func=f, uplevel=1)
    argspec = getfullargspec(func)
    #print(argspec)
    argvals = getargvalues(frame)
    print("debug info at " + file + ': ' + str(line))
    print(func_name + ':' + str(argvals))   ## reformat to pretty print arg values here
    return func_name



def df_dbg_prt_func_args(f):
    """Decorator: dpg_prt_func_args - Prints function name and arguments

    """
    def wrapped(*args, **kwargs):
        debug_prt_func_args(f) 
        return f(*args, **kwargs) 
    return wrapped

使用方法:

@df_dbg_prt_func_args
def leaf_decor(*args, **kwargs):
    """Leaf level, simple function"""
    print("in leaf")


def leaf_explicit(*args, **kwargs):
    """Leaf level, simple function"""
    debug_prt_func_args()
    print("in leaf")


def complex():
    """A complex function"""
    print("start complex")
    leaf_decor(3,4)
    print("middle complex")
    leaf_explicit(12,45)
    print("end complex")


complex()

并打印:

start complex
debug info at debug.py: 54
leaf_decor:ArgInfo(args=[], varargs='args', keywords='kwargs', locals={'args': (3, 4), 'f': <function leaf_decor at 0x2aaaac048d98>, 'kwargs': {}})
in leaf
middle complex
debug info at debug.py: 67
leaf_explicit:ArgInfo(args=[], varargs='args', keywords='kwargs', locals={'args': (12, 45), 'kwargs': {}})
in leaf
end complex

装饰器有点作弊:因为在wrapped中我们得到与函数本身相同的参数,所以在getfuncdebug_prt_func_args中找到并报告wrapped的ArgSpec并不重要。这段代码可能需要美化一下,但是对于我使用的简单调试测试用例来说,它已经可以正常工作了。

你可以做另一个小技巧:如果你取消注释getfunc中的for循环,你会发现inspect可以给你"上下文",实际上是函数被调用的源代码行。这段代码显然没有显示任何传递给你的函数的变量内容,但有时知道在你调用的函数上面一级使用的变量名称已经足够帮助你了。

如你所见,使用装饰器,你不必改变函数内部的代码。

可能你想要漂亮地打印参数。我在函数中留下了原始打印(还有一条被注释掉的打印语句),这样更容易玩耍。


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