python - Método print() para imprimir la expresión pasada literalmente junto con la salida calculada para una depuración rápida
debugging logging (9)
Deseo poder realizar la depuración de python usando print () o un método similar donde imprima la expresión pasada además de la salida habitual.
Por ejemplo, para el siguiente código:
print(42 + 42)
print(type(list))
print(datetime.now())
Salida de corriente:
84
<class ''type''>
2019-08-15 22:43:57.805861
Rendimiento esperado:
42 + 42 : 84
type(list) : <class ''type''>
datetime.now() : 2019-08-15 22:43:57.805861
Actualmente, se puede lograr lo mismo agregando manualmente la cadena de expresión, (no es tan elegante y viola el principio DRY).
print("42 + 42 : ", 42 + 42)
print("type(list) : ", type(list))
print("datetime.now() : ", datetime.now())
Intenté anular la impresión incorporada, pero sin éxito:
import builtins
def print(*args, **kwargs):
return builtins.print(*args, **kwargs) # passed expression isn''t available here as string!
¿Hay una manera de lograr esto? ¡Gracias!
Cuando llama al método print, los argumentos pasados no se evalúan mediante el método print, se evalúan antes de pasar al método print como argumento.
print(42 + 42) => print(84)
print(type(list)) => print(<type ''type''>)
print(datetime.now()) => print(datetime.datetime(2019, 8, 15, 23, 9, 50, 619157))
El método de impresión interna simplemente convierte el objeto dado en una cadena al llamar al método __str __ () del objeto pasado
En general, creo que si te encuentras usando
eval
, probablemente haya una mejor manera de hacer lo que intentas hacer, pero:
for statement in ["42 + 42", "type(list)", "datetime.now()"]:
print("{} : {}".format(statement, eval(statement))
Las cadenas f admitirán algo como esto en Python 3.8 (actualmente en versión beta).
De los docs :
Una cadena f como f ''{expr =}'' se expandirá al texto de la expresión, un signo igual, luego la representación de la expresión evaluada. Por ejemplo:
>>> user = ''eric_idle''
>>> member_since = date(1975, 7, 31)
>>> f''{user=} {member_since=}''
"user=''eric_idle'' member_since=datetime.date(1975, 7, 31)"
Los especificadores de formato de cadena f habituales permiten un mayor control sobre cómo se muestra el resultado de la expresión:
>>> delta = date.today() - member_since
>>> f''{user=!s} {delta.days=:,d}''
''user=eric_idle delta.days=16,075''
El especificador = mostrará la expresión completa para que se puedan mostrar los cálculos:
>>> print(f''{theta=} {cos(radians(theta))=:.3f}'')
theta=30 cos(radians(theta))=0.866
Puede definir una función de
superprint
e imprimirla y luego evaluar una cadena:
from datetime import datetime
def superprint(str):
print(str," : ",eval(str))
a = "42 + 42"
b = "type(list)"
c = "datetime.now()"
superprint(a)
superprint(b)
superprint(c)
SALIDA
42 + 42 : 84
type(list) : <class ''type''>
datetime.now() : 2019-08-15 14:44:43.072780
Si puede vivir arrojando todo lo que desea imprimir entre comillas, esto podría funcionar para usted.
Puede usar https://github.com/cool-RR/PySnooper
In [1]: from datetime import datetime
In [2]: import pysnooper
In [3]: @pysnooper.snoop()
...: def output():
...: print(42 + 42)
...: print(type(list))
...: print(datetime.now())
...:
In [4]: output()
Source path:... <ipython-input-3-d5732f8e9c36>
22:14:08.934915 call 2 def output():
22:14:08.935031 line 3 print(42 + 42)
84
22:14:08.935061 line 4 print(type(list))
<class ''type''>
22:14:08.935083 line 5 print(datetime.now())
2019-08-25 22:14:08.935100
22:14:08.935109 return 5 print(datetime.now())
Return value:.. None
Puede usar el módulo de
inspect
para obtener la línea de origen (
code_context
) de la persona que llama:
from inspect import getframeinfo, currentframe
def vprint(value):
caller = currentframe().f_back
info = getframeinfo(caller)
label = ''''.join(info.code_context).strip()
label = label.replace(''vprint('', '''')[:-1].strip()
print(label, ''='', value)
>>> vprint(12 + 3)
12 + 3 = 15
>>> vprint(type(list))
type(list) = <type ''type''>
>>> vprint(lambda x: x + 1)
lambda x: x + 1 = <function <lambda> at 0x7f93c104b9b0>
Funcionará bien solo para evaluaciones de una sola línea.
Como
code_context
devuelve solo la línea ejecutada (y no toda la instrucción), esto puede suceder:
>>> vprint([''''] +
...: [''a'', ''b''])
[''a'', ''b''] = ['''', ''a'', ''b'']
>>> vprint(math.log(
...: 2 * math.pi))
2 * math.pi) = 1.83787706641
Nota: romper la línea con
/
corrige esto (la sangría seguirá siendo extraña):
>>> vprint(math.log( /
...: 2 * math.pi))
math.log( 2 * math.pi) = 1.83787706641
Simplemente use el rastreo y busque el argumento de llamada.
La ventaja de esta solución es que no tiene que poner la expresión entre paréntesis.
import re
import traceback
def prnt_expression(expression):
for s in traceback.format_stack():
match = re.search(''prnt_expression/((.*)/)'', s)
if match:
expression_string = match.group(1)
break
print(f''{expression_string} : {expression}'')
llámalo así:
prnt_expression(42 + 42)
Usando Augusto Men''s Answer como base, el siguiente anula el método de impresión incorporada de python (). Esto ayudará a realizar la depuración sin requerir ningún cambio en el código bajo prueba, simplemente agregue esta definición de función en la parte superior de cualquier archivo y funcionará.
from inspect import getframeinfo, currentframe
import builtins
def print(*args, **kwargs):
info = getframeinfo(currentframe().f_back)
label = ''''.join(info.code_context).strip()
label = label.replace(''print('', '''', 1)[:-1].strip() # (optional)
return builtins.print(label, '':'', *args, **kwargs)
print(42 + 42)
print(type(list))
print(datetime.now())
print([i for i in range(5)])
if 1 < 2: print(''True'')
Salida:
42 + 42 : 84 type(list) : <class ''type''> datetime.now() : 2019-08-28 16:00:10.812306 [i for i in range(5)] : [0, 1, 2, 3, 4] if 1 < 2: ''True'' : True
Editar: Lo siento, mi respuesta no es realmente correcta, consulte @Augusto Men.
Bueno, nada es imposible en Python, pero usar
eval
no siempre puede funcionar:
import inspect
from datetime import datetime
def my_print(a):
assert(callable(a))
source = inspect.getsource(a).replace("my_print(lambda:", "").strip()[:-1]
print(source + " : " + str(a()))
# pass lambda function to my_print
print("my_print_result:")
my_print(lambda: 42 + 42)
my_print(lambda: type(list))
my_print(lambda: datetime.now())
def eval_print(s):
print(s + " : " + str(eval(s)))
# another way is passing string to eval_print
# but eval has its own special evaluation rules
# which will not work as expected when used in function
print("/neval_print result:")
eval_print("42 + 42")
eval_print("type(list)")
eval_print("datetime.now()")
def test():
local_test_1 = 1
my_print(lambda: local_test_1 + local_test_1)
eval_print("local_test_1 + local_test_1")
print("/ntest in function:")
test()
salida:
my_print_result:
42 + 42 : 84
type(list) : <class ''type''>
datetime.now() : 2019-08-26 07:06:30.550408
eval_print result:
42 + 42 : 84
type(list) : <class ''type''>
datetime.now() : 2019-08-26 07:06:30.551110
test in function:
local_test_1 + local_test_1 : 2
NameError: name ''local_test_1'' is not defined
Cuando use
eval
en la función, tendremos
NameError
, porque
eval
en Python tiene una regla especial:
Las funciones
eval()
yexec()
no tienen acceso al entorno completo para resolver nombres. Los nombres pueden resolverse en los espacios de nombres locales y globales de la persona que llama. Las variables libres no se resuelven en el espacio de nombres adjunto más cercano, sino en el espacio de nombres global.
referenciado desde 4. Modelo de ejecución: documentación de Python 3.7.4