打印变量的名称和值

117
在调试时,我们经常会看到类似这样的打印语句:
print x        # easy to type, but no context
print 'x=',x   # more context, harder to type
12
x= 12

如何编写一个函数,该函数接受一个变量或变量名,并打印出其名称和值?我只对调试输出感兴趣,这不会被纳入生产代码中。
debugPrint(x)    #  or
debugPrint('x')
x=12

1
打印(“%s = %s”%(名称,eval(name)))(呃)? - Blorgbeard
1
可能是重复的问题:如何在Python中打印变量名? - Andy
5
不要这样做!使用调试器! :) - jme
2
@jme,可惜的是,我的程序并不在有屏幕的计算机上运行,所以日志记录是我唯一的选择! - Mark Harrison
4
@Andy 我们一天前或许同意这是重复情况了。但是现在我学到了一个功能,它能明确地打印变量名和值:https://dev59.com/UVwY5IYBdhLWcg3w9r7a#57225950 标记为重复可能会存在这种风险 :-) - Ciro Santilli OurBigBook.com
显示剩余4条评论
14个回答

290

Python 3.8 f-string = syntax

终于来了!

#!/usr/bin/env python3
foo = 1
bar = 2
print(f"{foo=} {bar=}")

输出:

foo=1 bar=2 

添加了提交https://github.com/python/cpython/commit/9a4135e939bc223f592045a38e0f927ba170da32,“使用'='添加 f-string 调试”记录的内容:

f-strings now support =  for quick and easy debugging
-----------------------------------------------------

Add ``=`` specifier to f-strings. ``f'{expr=}'`` expands
to the text of the expression, an equal sign, then the repr of the
evaluated expression.  So::

  x = 3
  print(f'{x*9 + 15=}')

Would print ``x*9 + 15=42``.

所以它也适用于任意表达式。太好了!

梦想:从变量名字中获取类似JavaScript的字典键

我发现Python在几乎每一个方面都比JavaScript更好,但我真的很喜欢这个JavaScript功能:

let abc = 1
let def = 2
console.log({abc, def})

在JavaScript中可以工作,因为{abc,def}扩展为{abc:1,def:2}。这太棒了,并且除了日志记录之外,在代码的其他地方经常使用。

目前在Python中除了使用locals之外,无法很好地实现: Python variables as keys to dict


2
技术上讲,它起作用了,但是print(f'{foo=}')比OP要求的some(foo)some('foo')多出了很多标点符号。这看起来很傻,但重点是要有一个非常简单的东西,而且这个解决方案容易弄错标点符号,从我的角度来看影响效果。 - Stéphane Gourichon
@StéphaneGourichon 是的,这对于大型表达式尤其有效。 - Ciro Santilli OurBigBook.com
此外,您还可以获得一个 pylint(logging-fstring-interpolation) - Wtower
只是为了补充一下,该功能的名称是“自描述表达式:”https://docs.python.org/3/whatsnew/3.8.html#bpo-36817-whatsnew - Zack Light
如何循环遍历kwargs列表? - markling
@markling hwargs是一个dict,所以通过调用它的__str__print(f'{kwargs=}')会给出类似于kwargs={'a': 1, 'b': 2}的输出。缺少什么? - Ciro Santilli OurBigBook.com

32

您可以直接使用eval

def debug(variable):
    print variable, '=', repr(eval(variable))

更一般地说(在调用函数的上下文中实际有效,并且不会在debug('variable')上出现问题,但仅限于CPython):

from __future__ import print_function

import sys

def debug(expression):
    frame = sys._getframe(1)

    print(expression, '=', repr(eval(expression, frame.f_globals, frame.f_locals)))

而且你可以做:

>>> x = 1
>>> debug('x + 1')
x + 1 = 2

2
为什么会有踩票?如果是关于eval的安全问题,我不认为这是个问题,因为这永远不会出现在生产代码中。 - Eli Rose
非常简单且有效的解决方案,正是我所需要的。 - Oleg
在这种情况下不起作用:m = [1,2],debug(len(m)),它返回TypeError:len()需要恰好一个参数(给出了2个)。 - chikitin
11
这在3.8中已被f-string '='语法所取代,你应该提到这一点。 - smci
1
关于@smci提到的"f-string '='语法",请参见https://dev59.com/UVwY5IYBdhLWcg3w9r7a#57225950。 - Stéphane Gourichon
显示剩余3条评论

19

例如,在Python3.8中使用最新的f'{var = }'特性:

>>> a = 'hello'
>>> print(f'{a = }')
a = 'hello'

3
这个如何转化为像“debug(var)”这样的函数? - David Doria

6
import inspect
import re
def debugPrint(x):
    frame = inspect.currentframe().f_back
    s = inspect.getframeinfo(frame).code_context[0]
    r = re.search(r"\((.*)\)", s).group(1)
    print("{} = {}".format(r,x))

这种方法并不适用于所有的Python版本:

inspect.currentframe()

CPython实现细节: 该函数依赖解释器中对Python栈帧的支持,但并不是所有Python实现都保证存在该支持。如果运行在没有Python栈帧支持的实现中,该函数将返回None。


5
我刚刚开发了@Padraic Cunningham的答案,可以处理任意数量的变量。我喜欢这种方法,因为它的工作方式就像print(x1, x2, x3)一样-不需要用''包装变量名。
import inspect
import re

def prinfo(*args):
    frame = inspect.currentframe().f_back
    s = inspect.getframeinfo(frame).code_context[0]
    r = re.search(r"\((.*)\)", s).group(1)
    vnames = r.split(", ")
    for i,(var,val) in enumerate(zip(vnames, args)):
        print(f"{var} = {val}")
    
x1 = 1
x2 = 2
x3 = 3
prinfo(x1, x2, x3)

输出结果为:
x1 = 1
x2 = 2
x3 = 3

3
我编写了以下内容,以便能够像下面这样输入(在文件describe.py的第41行):
describe('foo' + 'bar')
describe(numpy.zeros((2, 4)))

并查看:

describe.py@41 describe('foo' + 'bar') = str(foobar) [len=6]   
describe.py@42 describe(numpy.zeros((2, 4))) = ndarray(array([[0., 0., 0., 0.],
   [0., 0., 0., 0.]])) [shape=(2, 4)]

Here's how:

# Print the line and filename, function call, the class, str representation and some other info

# Inspired by https://dev59.com/rWox5IYBdhLWcg3w95A9#8856387
import inspect
import re


def describe(arg):
    frame = inspect.currentframe()
    callerframeinfo = inspect.getframeinfo(frame.f_back)
    try:
        context = inspect.getframeinfo(frame.f_back).code_context
        caller_lines = ''.join([line.strip() for line in context])
        m = re.search(r'describe\s*\((.+?)\)$', caller_lines)
        if m:
            caller_lines = m.group(1)
            position = str(callerframeinfo.filename) + "@" + str(callerframeinfo.lineno)

            # Add additional info such as array shape or string length
            additional = ''
            if hasattr(arg, "shape"):
                additional += "[shape={}]".format(arg.shape)
            elif hasattr(arg, "__len__"):  # shape includes length information
                additional += "[len={}]".format(len(arg))

            # Use str() representation if it is printable
            str_arg = str(arg)
            str_arg = str_arg if str_arg.isprintable() else repr(arg)

            print(position, "describe(" + caller_lines + ") = ", end='')
            print(arg.__class__.__name__ + "(" + str_arg + ")", additional)
        else:
            print("Describe: couldn't find caller context")

    finally:
        del frame
        del callerframeinfo

https://gist.github.com/HaleTom/125f0c0b0a1fb4fbf4311e6aa763844b


1
我在JupyterLab中这样做,以获得类似Matlab的变量打印:
def debug(var):
    stack = traceback.extract_stack()
    filename, lineno, function_name, name = stack[-2]
    print(name[6:-1] , ':' , var)

用法:

x=1
debug(x)

yields:

x : 1

实际上,我使用的准确代码是为了方便输入和输出格式的清晰度。如果您想以完全相同的方式使用它,请使用以下代码:

import traceback
def p(var):
    stack = traceback.extract_stack()
    filename, lineno, function_name, name = stack[-2]
    print( "{:<25}".format(name[2:-1]) ,  ':   ' , var)

实际上,我将函数称为p而不是debug,以获得额外的便利。然后,您只需要将打印行从:print(name[6:-1] , ':' , var)更改为:print(name[2:-1] , ':' , var)。享受吧! - Abe Gertler

1

对于那些尚未使用Python 3.8的人,这里提供一种替代方案。

这是一个修改后的、更短的答案,来自于2009年关闭的重复问题,该问题在下面的链接中找到(在2015年8月14日以下也出现了错误的复制,错误在于re包含硬编码的函数名'varname'而不是所示的函数名'getm')。原始链接在此处: 如何在Python中打印变量名?

为了解释下面的re,inspect.getframeinfo(inspect.currentframe(), f_back)[3]会返回一个列表,其中包含函数签名。

['                p(prev)\n']

将其转换为字符串可以避免循环遍历仅有一个元素的列表。re查找需要转义的'(',接下来的'('用于创建匹配内部的组,然后[^)]表示除')'外的任意字符,'^'在此上下文中表示“非”,方括号[]表示匹配其中任何字符,随后的'*'是0次或多次的量化器。然后使用')'关闭组,匹配结束的')',完成!
def p(x):
    import inspect
    import re
    m = re.search('\(([^)]*)\)',str(inspect.getframeinfo(inspect.currentframe().f_back)[3]))
    print(f' {m.group(1)}: {x}')

这个能在2.7版本上工作吗?等我检查一下...看起来不行。我看到了一两个其他的变化,没有使用inspect.getframeinfo(inspect.currentframe().f_back)[3],所以也许其中一个会起作用。你需要检查重复项并仔细查看答案。 还要注意的是,有些答案说要注意可能与各种解决方案不兼容的python解释器。以上内容适用于

Python 3.6.4 (v3.6.4:d48ecebad5, Dec 18 2017, 21:07:28)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] 在 darwin 上


0
一个简单的例子是:
def debugPrint(*expr):
    text = traceback.extract_stack()[-2][3]
    begin = text.find('debugPrint(') + len('debugPrint(')
    end = text.find(')',begin)
    text=[name.strip() for name in text[begin:end].split(',')]
    for t, e in text, expr:
        print(str(t) +  " = " + str(e))

希望能对你有所帮助!


0

当从变量的值中查找变量名时,
您可能有几个变量等于相同的值,
例如var1 = 'hello'和var2 = 'hello'。

我对您的问题的解决方案:

def find_var_name(val):

    dict_list = []
    global_dict = dict(globals())

    for k, v in global_dict.items():
        dict_list.append([k, v])
   
    return [item for item in dict_list if item[1] == val]

var1 = 'hello'
var2 = 'hello'
find_var_name('hello')

输出

[['var1', 'hello'], ['var1', 'hello']]

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