Pol*_*ics 9 python namespaces decorator python-decorators
为了帮助我调试我编写的一些代码,我想创建一个函数装饰器,在创建或修改每个变量时打印变量的名称及其值,基本上给我一个"逐个播放"视图当我调用该函数时会发生什么.
到目前为止,我一直在使用的方法只是添加一条线,就像print(foo)
我想看到发生了什么,但这非常耗时,使我的代码看起来很混乱(可能是非Pythonicness的缩影).
实际上,我想做的是:
@show_guts
def foo(a,b):
biz = str(a)
baz = str(b)
return biz + baz
foo("banana","phone")
Run Code Online (Sandbox Code Playgroud)
在IDE中打印这样的东西:
biz = "banana"
baz = "phone"
bananaphone
Run Code Online (Sandbox Code Playgroud)
我的问题是@show_guts
看起来像什么.我知道可以只打印装饰器的值a
和b
def print_args(function):
def wrapper(*args,**kwargs):
print("Arguments:",args)
print("Keyword Arguments:",kwargs)
return function(*args,**kwargs)
return wrapper
Run Code Online (Sandbox Code Playgroud)
这给了我
Arguments: ('banana', 'phone')
Keyword Arguments: {}
'bananaphone'
Run Code Online (Sandbox Code Playgroud)
但我完全不知道如何打印局部变量名称及其值.更不用说以"整洁"的方式做到这一点.
Mar*_*ers 10
如果不启用跟踪,则无法执行此操作; 这会伤害表现.调用函数时会构造函数本地,并在函数返回时进行清理,因此没有其他方法可以从装饰器访问这些本地函数.
您可以使用插入跟踪函数sys.settrace()
,然后响应Python解释器发送该函数的事件.我们想要做的是跟踪只是装饰作用,并记录当地人当函数返回:
import sys
import threading
def show_guts(f):
sentinel = object()
gutsdata = threading.local()
gutsdata.captured_locals = None
gutsdata.tracing = False
def trace_locals(frame, event, arg):
if event.startswith('c_'): # C code traces, no new hook
return
if event == 'call': # start tracing only the first call
if gutsdata.tracing:
return None
gutsdata.tracing = True
return trace_locals
if event == 'line': # continue tracing
return trace_locals
# event is either exception or return, capture locals, end tracing
gutsdata.captured_locals = frame.f_locals.copy()
return None
def wrapper(*args, **kw):
# preserve existing tracer, start our trace
old_trace = sys.gettrace()
sys.settrace(trace_locals)
retval = sentinel
try:
retval = f(*args, **kw)
finally:
# reinstate existing tracer, report, clean up
sys.settrace(old_trace)
for key, val in gutsdata.captured_locals.items():
print '{}: {!r}'.format(key, val)
if retval is not sentinel:
print 'Returned: {!r}'.format(retval)
gutsdata.captured_locals = None
gutsdata.tracing = False
return retval
return wrapper
Run Code Online (Sandbox Code Playgroud)
示范:
>>> @show_guts
... def foo(a,b):
... biz = str(a)
... baz = str(b)
... return biz + baz
...
>>> result = foo("banana","phone")
a: 'banana'
biz: 'banana'
b: 'phone'
baz: 'phone'
Returned: 'bananaphone'
Run Code Online (Sandbox Code Playgroud)