【问题标题】:Printing names of variables passed to a function打印传递给函数的变量名称
【发布时间】:2012-04-13 20:23:46
【问题描述】:

在某些情况下,我想像这样打印调试样式的输出:

# 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

我认为应该可以通过使用检查模块来定位堆栈帧,然后找到适当的行,在该行中查找源代码,从那里获取参数的名称。函数名可以通过向上移动一个堆栈帧来获得。 (参数的值很容易获得:它们直接传递给函数debug。)

我在正确的轨道上吗?有什么食谱可以参考吗?

【问题讨论】:

    标签: python debugging python-3.x introspection


    【解决方案1】:

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

    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
    

    【讨论】:

    • 这很好很简单!但是,如果这意味着我不必重复每个变量名称两次:一次作为关键字参数名称,一次作为关键字参数值,我不介意更复杂。
    • 您可以尝试拨打debug(**locals())
    【解决方案2】:

    您通常做对了,尽管使用 AOP 来完成这类任务会更容易。基本上,不用每次对每个变量都调用“调试”,您可以只使用在某些事件上执行某些操作的方面来装饰代码,例如在输入函数以打印传递的变量及其名称时。

    请参考this网站和旧所以post了解更多信息。

    【讨论】:

      【解决方案3】:

      是的,你在正确的轨道上。您可能想查看inspect.getargspec,它会返回一个命名元组,其中包含 args、varargs、关键字、传递给函数的默认值。

      import inspect
      
      def f():
        a = 5
        b = 8
        debug(a, b)
      
      
      def debug(a, b):
          print inspect.getargspec(debug)
      f()
      

      【讨论】:

        【解决方案4】:

        这真的很棘手。让我尝试重用this code 给出一个更完整的答案,以及 Senthil 的答案中关于getargspec 的提示,这让我不知何故被触发了。顺便说一句,getargspec 在 Python 3.0 中已弃用,而getfullarcspec should be used 则改为。

        这适用于我在 Python 3.1.2 上显式调用调试函数和使用装饰器:

        # from: https://stackoverflow.com/a/4493322/923794
        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-loop,您会看到inspect 可以为您提供“上下文”,这实际上是调用函数的源代码行。这段代码显然没有显示给你的函数的任何变量的内容,但有时它已经有助于知道使用的变量名比你调用的函数高一级。

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

        您可能想要漂亮地打印参数。我在函数中保留了原始打印(以及注释掉的打印语句),因此更容易使用。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-07-21
          • 1970-01-01
          • 2014-08-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-12-31
          • 2013-06-04
          相关资源
          最近更新 更多