【问题标题】:How to find the name of a variable that was passed to a function?如何找到传递给函数的变量的名称?
【发布时间】:2012-01-13 19:17:32
【问题描述】:

在 C/C++ 中,我经常发现在调试时定义一个宏很有用,比如ECHO(x),它会打印出变量名及其值(即ECHO(variable) 可能会打印出variable 7)。您可以使用“字符串化”运算符# 在宏中获取变量名称,如here 所述。有没有办法在 Python 中做到这一点?

换句话说,我想要一个函数

def echo(x):
    #magic goes here

如果被称为foo=7; echo(foo)(或foo=7; echo('foo'),也许),将打印出foo 7。我意识到如果我将变量及其名称都传递给函数,这样做是微不足道的,但是我在调​​试时经常使用这样的函数,而且重复总是让我很恼火。

【问题讨论】:

    标签: python variables reflection


    【解决方案1】:

    不是真正的解决方案,但可能很方便(无论如何你有 echo('foo') 有问题):

    def echo(**kwargs):
        for name, value in kwargs.items():
            print name, value
    
    foo = 7
    echo(foo=foo)
    

    更新:echo(foo)inspect 的解决方案

    import inspect
    import re
    
    def echo(arg):
        frame = inspect.currentframe()
        try:
            context = inspect.getframeinfo(frame.f_back).code_context
            caller_lines = ''.join([line.strip() for line in context])
            m = re.search(r'echo\s*\((.+?)\)$', caller_lines)
            if m:
                caller_lines = m.group(1)
            print caller_lines, arg
        finally:
            del frame
    
    foo = 7
    bar = 3
    baz = 11
    echo(foo)
    echo(foo + bar)
    echo((foo + bar)*baz/(bar+foo))
    

    输出:

    foo 7
    foo + bar 10
    (foo + bar)*baz/(bar+foo) 11
    

    它的调用最小,但对换行很敏感,例如:

    echo((foo + bar)*
          baz/(bar+foo))
    

    将打印:

    baz/(bar+foo)) 11
    

    【讨论】:

    • 第二种解决方案非常聪明。在研究它的工作原理时,我发现了一个重复的问题here - 尽管您的正则表达式比那里给出的类似答案更强大。
    • 很好的答案,谢谢。您能简要解释一下正则表达式的工作原理吗?
    • @Ferguzz,它搜索echo,然后搜索\s* - 0 个或多个空格,\( - 括号,(.+?)\)$ - 非贪婪搜索\) 之前的所有符号(括号)在行尾($)。更多信息regular-expressions.info/repeat.html。你也可以debug regexps。在交互式 Python 会话中尝试:re.compile(r'echo\s*\((.+?)\)$', re.DEBUG)
    【解决方案2】:
    def echo(x):
        import inspect
        print "{0}: {1}".format(x, inspect.stack()[1][0].f_locals[x])
    y = 123
    echo('y')
    # 'y: 123'
    

    另请参阅:https://stackoverflow.com/a/2387854/16361

    请注意,这可能会导致 GC 问题:

    http://docs.python.org/library/inspect.html#the-interpreter-stack

    它还会关闭那些因弄乱框架而被烧伤的人,并且可能会在你的嘴里留下不好的味道。但它会起作用。

    【讨论】:

      【解决方案3】:

      这是一个解决方案,您可以输入更多内容来调用它。它依赖于locals 内置函数:

      def print_key(dictionary, key):
          print key, '=', dictionary[key]
      
      
      foo = 7
      print_key(locals(), 'foo')
      

      使用inspect 模块也可以使用您提到的语义的echo。但是,请阅读检查文档中的警告。这是一个丑陋的不可移植的 hack(它不适用于 Python 的所有实现)。请务必仅将其用于调试。

      这个想法是查看调用函数的locals。 inspect 模块允许这样做:调用由由f_back 属性链接在一起的框架对象表示。每个框架的局部和全局变量都可用(也有内置变量,但您不太可能需要打印它们)。

      您可能想要显式删除任何引用框架对象以防止引用循环,如inspect docs 中所述,但这并非绝对必要 - 垃圾回收迟早会释放它们。

      import inspect
      
      def echo(varname):
          caller = inspect.currentframe().f_back
          try:
              value = caller.f_locals[varname]
          except KeyError:
              value = caller.f_globals[varname]
          print varname, '=', value
          del caller
      
      foo = 7
      echo('foo')
      

      【讨论】:

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