Метод print() для печати переданного выражения буквально вместе с вычисленным выводом для быстрой отладки
Я хотел бы иметь возможность выполнять отладку Python, используя print() или аналогичный метод, где он печатает переданное выражение в дополнение к обычному выводу.
Например, для следующего кода:
print(42 + 42)
print(type(list))
print(datetime.now())
Текущий вывод:
84
<class 'type'>
2019-08-15 22:43:57.805861
Ожидаемый результат:
42 + 42 : 84
type(list) : <class 'type'>
datetime.now() : 2019-08-15 22:43:57.805861
В настоящее время то же самое может быть достигнуто путем добавления строки выражения вручную (не очень элегантно, imho и нарушает принцип DRY).
print("42 + 42 : ", 42 + 42)
print("type(list) : ", type(list))
print("datetime.now() : ", datetime.now())
Я пытался переопределить встроенную печать, но безуспешно:
import builtins
def print(*args, **kwargs):
return builtins.print(*args, **kwargs) # passed expression isn't available here as string!
Есть ли способ достичь этого? Спасибо!
Ответы
Ответ 1
Используя Augusto Men Answer в качестве основы, следует переопределить встроенный метод python print(). Это поможет выполнить отладку без каких-либо изменений в тестируемом коде, просто добавьте это определение функции поверх любого файла, и он будет работать.
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')
Выход:
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
Ответ 2
f-strings будет поддерживать что-то подобное в Python 3.8 (в настоящее время в бета-версии).
Из документов:
F-строка, такая как f '{expr =}', расширится до текста выражения, знака равенства, а затем представления вычисленного выражения. Например:
>>> user = 'eric_idle'
>>> member_since = date(1975, 7, 31)
>>> f'{user=} {member_since=}'
"user='eric_idle' member_since=datetime.date(1975, 7, 31)"
Обычные описатели формата f-строки позволяют лучше контролировать отображение результата выражения:
>>> delta = date.today() - member_since
>>> f'{user=!s} {delta.days=:,d}'
'user=eric_idle delta.days=16,075'
Спецификатор = отобразит все выражение, чтобы можно было увидеть вычисления:
>>> print(f'{theta=} {cos(radians(theta))=:.3f}')
theta=30 cos(radians(theta))=0.866
Ответ 3
Обычно я думаю, что если вы используете eval
, возможно, есть лучший способ сделать то, что вы пытаетесь сделать, но:
for statement in ["42 + 42", "type(list)", "datetime.now()"]:
print("{} : {}".format(statement, eval(statement))
Ответ 4
Вы можете определить функцию superprint
и распечатать ее, а затем вычислить строку:
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)
OUTPUT
42 + 42 : 84
type(list) : <class 'type'>
datetime.now() : 2019-08-15 14:44:43.072780
Если вы можете скинуть все, что хотите напечатать в кавычках, это может сработать для вас.
Ответ 5
Вы можете использовать модуль inspect для получения исходной строки (code_context
) от вызывающей стороны:
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>
Хорошо подойдет только для однострочных оценок. Поскольку code_context
возвращает только выполненную строку (а не всю инструкцию), это может произойти:
>>> vprint([''] +
...: ['a', 'b'])
['a', 'b'] = ['', 'a', 'b']
>>> vprint(math.log(
...: 2 * math.pi))
2 * math.pi) = 1.83787706641
Примечание: разрыв строки с \
исправляет это (хотя отступ будет выглядеть странно):
>>> vprint(math.log( \
...: 2 * math.pi))
math.log( 2 * math.pi) = 1.83787706641
Ответ 6
Когда вы вызываете метод print, переданные аргументы не оцениваются методом print, они оцениваются перед передачей в метод print в качестве аргумента.
print(42 + 42) => print(84)
print(type(list)) => print(<type 'type'>)
print(datetime.now()) => print(datetime.datetime(2019, 8, 15, 23, 9, 50, 619157))
Метод внутренней печати просто преобразует данный объект в строку, вызывая метод __str __() переданного объекта
Ответ 7
Изменение: Извините, мой ответ не совсем правильный, пожалуйста, смотрите @Augusto Men.
Ну, нет ничего невозможного в Python, но использование eval
не всегда может работать:
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()
выход:
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
При использовании eval
в функции у нас будет NameError
, потому что eval
в Python имеет специальное правило:
eval()
иexec()
функции не имеют доступа к полной среде для разрешения имен. имена может быть разрешен в локальных и глобальных пространствах имен вызывающей стороны. Free переменные не разрешаются в ближайшем окружающем пространстве имен, но в глобальное пространство имен.
ссылка из 4. Модель исполнения - документация Python 3.7.4
Ответ 8
Вы можете использовать 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
Ответ 9
Просто используйте трассировку и найдите аргумент вызова.
Преимущество этого решения заключается в том, что вам не нужно помещать выражение в круглые скобки.
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}')
называйте это так:
prnt_expression(42 + 42)