简体   繁体   English

Python:打印变量的名称和值?

[英]Python: Print a variable's name and value?

When debugging, we often see print statements like these:在调试的时候,我们经常会看到这样的打印语句:

print x        # easy to type, but no context
print 'x=',x   # more context, harder to type
12
x= 12

How can write a function that will take a variable or name of a variable and print its name and value?如何编写一个 function 来获取变量或变量名称并打印其名称和值? I'm interested exclusively in debugging output, this won't be incorporated into production code.我只对调试 output 感兴趣,这不会合并到生产代码中。

debugPrint(x)    #  or
debugPrint('x')
x=12

Python 3.8 f-string = syntax Python 3.8 f-string =语法

It has arrived!它已经到了!

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

output:输出:

foo=1 bar=2 

Added in commit https://github.com/python/cpython/commit/9a4135e939bc223f592045a38e0f927ba170da32 "Add f-string debugging using '='."在提交https://github.com/python/cpython/commit/9a4135e939bc223f592045a38e0f927ba170da32 中添加“使用‘=’添加 f 字符串调试。” which documents:哪些文件:

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``.

so it also works for arbitrary expressions.所以它也适用于任意表达式。 Nice!好的!

You can just use eval :你可以只使用eval

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

Or more generally (which actually works in the context of the calling function and doesn't break on debug('variable') , but only on CPython):或者更一般地(它实际上在调用函数的上下文中工作并且不会在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)))

And you can do:你可以这样做:

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

Use the latest f'{var = }' feature in Python3.8 for example:例如使用 Python3.8 中最新的f'{var = }'特性:

>>> a = 'hello'
>>> print(f'{a = }')
a = 'hello'
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))

This won't work for all versions of python:这不适用于所有版本的python:

inspect.currentframe()检查当前帧()

CPython implementation detail: This function relies on Python stack frame support in the interpreter, which isn't guaranteed to exist in all implementations of Python. CPython 实现细节:此函数依赖于解释器中的 Python 堆栈帧支持,不能保证在 Python 的所有实现中都存在。 If running in an implementation without Python stack frame support this function returns None.如果在没有 Python 堆栈框架支持的实现中运行,则此函数返回 None。

I wrote the following to be able to type something like (at line 41 of file describe.py ):我写了以下内容以便能够输入类似的内容(在文件describe.py第 41 行):

describe('foo' + 'bar')
describe(numpy.zeros((2, 4)))

and see:并看到:

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://stackoverflow.com/a/8856387/5353461
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 https://gist.github.com/HaleTom/125f0c0b0a1fb4fbf4311e6aa763844b

For those who are not using python 3.8 yet, here is an alternative.对于那些还没有使用 python 3.8 的人,这里有一个替代方案。

This is a modified, shorter version of the accepted answer from a closed 2009 duplicate question found here, (which was also copied with a mistake in the below Aug 14, '15, the mistake being the re contains the hard coded function name 'varname' instead of the function name shown 'getm').这是此处找到的一个已关闭的 2009 年重复问题的已接受答案的修改后的较短版本(在 15 年 8 月 14 日下面也被错误复制,错误是 re 包含硬编码的函数名称“varname” ' 而不是显示的函数名称 'getm')。 Original found here: How can you print a variable name in python?原文在这里: How can you print a variable name in python? ? ?

To explain the re below, the inspect.getframeinfo(inspect.currentframe(), f_back)[3] gives the function signature in a list为了解释下面的 re,inspect.getframeinfo(inspect.currentframe(), f_back)[3] 给出了列表中的函数签名

['                p(prev)\n']

Casting to str saves you from having to loop through the list of one item.强制转换为 str 使您不必循环遍历一个项目的列表。 The re looks for an '(' which has to be escaped, the next '(' is to create a group within the match to reference, then [^)] means any character not ')', the '^' means 'not' in this context, brackets [] mean match any character within, and the following '*' is a quantifier for 0 or more times. re 寻找一个 '(' 必须被转义,下一个 '(' 是在匹配中创建一个组来引用,然后 [^)] 表示任何不是 ')' 的字符,'^' 表示'不是' 在这种情况下,方括号 [] 表示匹配其中的任何字符,后面的 '*' 是 0 次或多次的量词。 Then close the group with a ')', match the closing ')' and voila:然后用 ')' 关闭组,匹配关闭的 ')' 并瞧:

def p(x):
    import inspect
    import re
    m = re.search('\(([^)]*)\)',str(inspect.getframeinfo(inspect.currentframe().f_back)[3]))
    print(f' {m.group(1)}: {x}')

Does this work with 2.7?这适用于 2.7 吗? Wait here while I check ... No, seemingly not.在这里等我检查……不,似乎不是。 I did see one or two other variations that didn't use inspect.getframeinfo(inspect.currentframe().f_back)[3], so perhaps one of those would work.我确实看到了一两个其他没有使用inspect.getframeinfo(inspect.currentframe().f_back)[3]的变体,所以也许其中一个可以工作。 You'd have to check the duplicates and comb through the answers.您必须检查重复项并梳理答案。 Also to caution, some answers said to beware of python interpreters that may not be compatible with various solutions.另外要注意的是,一些答案说要提防可能与各种解决方案不兼容的 python 解释器。 The above worked on以上工作

Python 3.6.4 (v3.6.4:d48ecebad5, Dec 18 2017, 21:07:28) Python 3.6.4(v3.6.4:d48ecebad5,2017 年 12 月 18 日,21:07:28)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] 在达尔文

Just developed the answer of @Padraic Cunningham to take arbitrary number of variables.刚刚开发了@Padraic Cunningham 的答案,以采用任意数量的变量。 I liked this method since it works just like print(x1, x2, x3) - no need to wrap var names in '' .我喜欢这种方法,因为它的工作原理就像print(x1, x2, x3) - 不需要在''包装 var 名称。

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)

Output is:输出是:

x1 = 1
x2 = 2
x3 = 3

I do it like this in jupyterlab to get matlab like variable printing:我在 jupyterlab 中这样做是为了像变量打印一样获得 matlab:

def debug(var):
    stack = traceback.extract_stack()
    filename, lineno, function_name, name = stack[-2]
    print(name[6:-1] , ':' , var)

usage:用法:

x=1
debug(x)

yields:产量:

x : 1

Actually the exact code I use, for typing convenience and cleanness of output format, in case you would like to use it the same exact way, is:实际上,为了输入 output 格式的方便和简洁,我使用的确切代码是:

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

Quite ugly , but does the job :相当丑陋,但工作:

import inspect, re
def getm(p):
  for line in inspect.getframeinfo(inspect.currentframe().f_back)[3]:
    match = re.search(r'\bvarname\s*\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)', line)
    if match:
      return match.group(1)
x=21
search = getm(x);
print (search , '=' , eval(search))

A simple example would be:一个简单的例子是:

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

Hope it helps!希望能帮助到你!

I've just concocted a function like this that prints an arbitrary expression:我刚刚编造了一个像这样打印任意表达式的函数:

import inspect, pprint

def pp(n):
    print()
    print(n,"=")
    f=inspect.stack()[1].frame
    pprint.pprint(eval(n,f.f_globals,f.f_locals))

(I used a blank line before the name and a newline before the value 'cuz in my case, I needed to print large data structures. It's easier to read such an output with the line breaks.) (我在名称之前使用了一个空行,在值 'cuz 之前使用了一个换行符,因为在我的情况下,我需要打印大型数据结构。使用换行符更容易阅读这样的输出。)

It's safe as long as you don't pass it untrusted input.只要您不传递不受信任的输入,它就是安全的。

You might also be interested in my dump module.您可能也对我的dump模块感兴趣。 It prints all the object's fields in a human-readable form.它以人类可读的形式打印所有对象的字段。 Proved extremely useful for debugging.证明对调试非常有用。

Multiple variables (taking @Blender response one step further) :多个变量(将@Blender 响应更进一步):

def debug(variables, sep =''):
        vars = variables.split(',')
        for var in vars:
          print(var, '=', repr(eval(var)), end = sep)

Example:例子:

import bumpy as np
gPrimeLinear = lambda z: np.ones(np.array(z).size)*z
gPrimeSigmoid = lambda z: 1./(1+np.exp(-z))*(1-1./(1+np.exp(-z)))
gPrimeTanh = lambda z: 1- np.tanh(z)**2
z = np.array([ 0.2, 0.4, 0.1])
debug("z, gPrimeLinear(z), gPrimeSigmoid(z), gPrimeTanh(z)", '\n')

This returns:这将返回:

> z = array([0.2, 0.4, 0.1])  
> gPrimeLinear(z) = array([0.2, 0.4, 0.1]) 
> gPrimeSigmoid(z) = array([0.24751657, 0.24026075, 0.24937604]) 
> gPrimeTanh(z) = array([0.96104298, 0.85563879, 0.99006629])

When finding the name of a variable from its value,当从变量的值中找到变量的名称时,
you may have several variables equal to the same value,您可能有多个变量等于相同的值,
for example var1 = 'hello' and var2 = 'hello'.例如 var1 = 'hello' 和 var2 = 'hello'。

My solution to your question:我对你的问题的解决方案:

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')

Outputs输出

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM