web-dev-qa-db-fra.com

Python: Affiche le nom et la valeur d'une variable?

Lors du débogage, nous voyons souvent des instructions print comme celles-ci:

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

Comment écrire une fonction qui va prendre une variable ou le nom d'une variable et imprimer son nom et sa valeur? Je m'intéresse exclusivement au débogage des sorties, elles ne seront pas intégrées au code de production.

debugPrint(x)    #  or
debugPrint('x')
x=12
26
Mark Harrison

Vous pouvez simplement utiliser eval:

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

Ou plus généralement (qui fonctionne réellement dans le contexte de la fonction appelante et ne se casse pas sur debug('variable'), mais uniquement sur 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)))

Et vous pouvez faire:

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

J'ai écrit ce qui suit pour pouvoir taper quelque chose comme (à la ligne 41 du fichier describe.py):

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

et voir:

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

Voici comment:

# 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__.__+ "(" + str_arg + ")", additional)
        else:
            print("Describe: couldn't find caller context")

    finally:
        del frame
        del callerframeinfo

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

3
Tom Hale
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))

Cela ne fonctionnera pas pour toutes les versions de python:

inspect.currentframe ()

Détail de l’implémentation CPython: Cette fonction repose sur le support des trames de pile Python dans l’interpréteur, qui n’est pas garanti dans toutes les implémentations de Python. Si elle est exécutée dans une implémentation sans support de pile Python, cette fonction renvoie Aucun.

2
Padraic Cunningham

Je viens de concocter une fonction comme celle-ci qui affiche une expression arbitraire:

import inspect, pprint

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

(J'ai utilisé une ligne vierge avant le nom et une nouvelle ligne avant la valeur 'cuz dans mon cas, il me fallait imprimer de grandes structures de données. Il est plus facile de lire une telle sortie avec les sauts de ligne.)

C'est sûr tant que vous ne passez pas une entrée non fiable.

Vous pourriez également être intéressé par mon dump module. Il imprime tous les champs de l'objet sous une forme lisible par l'homme. S'est avéré extrêmement utile pour le débogage.

0
ivan_pozdeev

Assez moche, mais fait le travail:

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))
0
Mohit Bhasi

Un exemple simple serait:

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

J'espère que ça aide!

0
Sujay Kumar