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