打印函数的局部变量名称和值

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看起来像什么.我知道可以只打印装饰器的值ab

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)