Python: печатать имя и значение переменной?

При отладке мы часто видим инструкции печати, подобные этим:

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

Вы можете просто использовать 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

Синтаксис F-строки = Python 3.8

Это прибыло!

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

выход:

foo=1 bar=2 

Добавлено в коммит https://github.com/python/cpython/commit/9a4135e939bc223f592045a38e0f927ba170da32 "Добавить отладку f-строки с помощью '='." какие документы:

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

так что это также работает для произвольных выражений. Приятно!

Ответ 3

Я написал следующее, чтобы можно было напечатать что-то вроде (в строке 41 файла describe.py):

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

и посмотри:

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

Вот как:

# 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

Ответ 4

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

Довольно уродливый, но выполняет задание:

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

Ответ 6

Простой пример:

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

Надеюсь, это поможет!

Ответ 7

Я только что создал такую функцию, которая печатает произвольное выражение:

import inspect, pprint

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

(в моем случае я использовал пустую строку перед именем и символ новой строки перед значением 'cuz, мне нужно было печатать большие структуры данных. Такой вывод легче читать с разрывами строк.)

Это безопасно, пока вы не передадите ему ненадежный ввод.

Вас также может заинтересовать мой dump модуль. Он печатает все поля объекта в удобочитаемой форме. Оказалось чрезвычайно полезным для отладки.