用于打印函数执行的每一行的 Python 装饰器

Posted

技术标签:

【中文标题】用于打印函数执行的每一行的 Python 装饰器【英文标题】:Python Decorator for printing every line executed by a function 【发布时间】:2015-11-16 18:18:51 【问题描述】:

出于调试目的,我想打印出与 Python 方法中执行的每一行相关的内容。

例如,如果行中有一些赋值,我想打印为该变量分配了什么值,如果有函数调用,我想打印出函数返回的值等。

因此,例如,如果我要使用装饰器,则应用于函数/方法,例如:

@some_decorator
def testing() : 
    a = 10
    b = 20
    c = a + b
    e = test_function()

调用函数测试时,应打印以下内容:

a = 10
b = 20  
c = 30
e = some_value

有什么方法可以实现吗?更根本的是,我想知道我是否可以编写一个可以逐行通过其他代码的代码,检查它是什么类型的指令等等。或者也许我们可以得到一本字典来找出所有变量一个类,我可以得到一个像数据结构这样的字典来获取函数中的每条指令,这和元程序一样好。

因此,我特别在寻找使用装饰器的解决方案,因为我很好奇是否可以拥有一个可以逐行遍历整个函数并逐行装饰它的装饰器, 但欢迎任何和所有解决方案。

提前致谢。

【问题讨论】:

可以,但我不知道。本质上,您可以从修饰函数中提取文件和行号,重新读取函数,将其编译为 AST,在 AST 中插入节点,然后编译 AST 并将其用作函数。一个有趣的项目。 也许pdb 模块会有所帮助? @perreal 您能否更详细地解释您的方法或提供一个链接,我可以在其中找到有关您方法中元素的更多信息,以及如何将这些元素应用于我的问题。顺便感谢您的回复 @ironstein:初学者请查看此内容tomforb.es/automatically-inline-python-function-calls 基本上你必须将你的函数包装在一个函数中,将其加载到调试器中,然后单步执行该函数。 【参考方案1】:

这样的事情怎么样?这对你有用吗?

调试上下文:

import sys

class debug_context():
    """ Debug context to trace any function calls inside the context """

    def __init__(self, name):
        self.name = name

    def __enter__(self):
        print('Entering Debug Decorated func')
        # Set the trace function to the trace_calls function
        # So all events are now traced
        sys.settrace(self.trace_calls)

    def __exit__(self, *args, **kwargs):
        # Stop tracing all events
        sys.settrace = None

    def trace_calls(self, frame, event, arg): 
        # We want to only trace our call to the decorated function
        if event != 'call':
            return
        elif frame.f_code.co_name != self.name:
            return
        # return the trace function to use when you go into that 
        # function call
        return self.trace_lines

    def trace_lines(self, frame, event, arg):
        # If you want to print local variables each line
        # keep the check for the event 'line'
        # If you want to print local variables only on return
        # check only for the 'return' event
        if event not in ['line', 'return']:
            return
        co = frame.f_code
        func_name = co.co_name
        line_no = frame.f_lineno
        filename = co.co_filename
        local_vars = frame.f_locals
        print ('  0 1 2 locals: 3'.format(func_name, 
                                                  event,
                                                  line_no, 
                                                  local_vars))

调试装饰器:

def debug_decorator(func):
    """ Debug decorator to call the function within the debug context """
    def decorated_func(*args, **kwargs):
        with debug_context(func.__name__):
            return_value = func(*args, **kwargs)
        return return_value
    return decorated_func

用法

@debug_decorator
def testing() : 
    a = 10
    b = 20
    c = a + b

testing()

输出

###########################################################
#output:
#   Entering Debug Decorated func
#     testing line 44 locals: 
#     testing line 45 locals: 'a': 10
#     testing line 46 locals: 'a': 10, 'b': 20
#     testing return 46 locals: 'a': 10, 'b': 20, 'c': 30
###########################################################

【讨论】:

这太方便了,应该标记为正确答案。 我们如何为类方法扩展它? 如何从行打印代码?

以上是关于用于打印函数执行的每一行的 Python 装饰器的主要内容,如果未能解决你的问题,请参考以下文章

如何让 Perl 和 Python 打印正在执行的程序的每一行?

用于包装尝试的通用装饰器除了在 python 中?

有没有办法在 Python 函数的每一行上装饰/执行操作?

python装饰器

Python 中的纯静态类 - 使用元类、类装饰器还是其他东西?

python读取一个文件的每一行判断是否为素数,并把结果写到另一个文件中