如何在调用时打印函数?

13 浏览
0 Comments

如何在调用时打印函数?

在调试Python脚本时,我非常希望能够了解整个程序的完整调用堆栈。如果Python有一个命令行标志可以在函数被调用时打印所有函数名就太理想了(我查看了man Python2.7,但没有找到类似的内容)。

由于这个脚本中的函数数量较多,如果可能的话,我不希望在每个函数和/或类的开头添加打印语句。

一个中间的解决办法是使用PyDev的调试器,在程序中设置几个断点,并检查给定点的调用堆栈,所以暂时我会采用这种方法。

如果存在这样的方法,我仍然更希望看到程序生命周期中所有调用的函数的完整列表。

0
0 Comments

问题的出现原因:

- traceit.py模块中使用了tracefunc函数来追踪函数的调用栈。

- 该模块中使用了sys.setprofile()方法将tracefunc函数设置为系统的profile函数,以便在每次函数调用时都会调用tracefunc函数。

- tracefunc函数会根据一定的条件打印出函数的信息,包括函数名、文件路径和行号等。

问题的解决方法:

- 导入traceit模块,并使用sys.setprofile()方法将tracefunc函数设置为系统的profile函数。

- 运行代码,每次函数调用时,都会打印出函数的信息。

文章整理如下:

我采用了kindall的答案并在此基础上进行了修改。我创建了以下模块:

"""traceit.py
Traces the call stack.
Usage:
import sys
import traceit
sys.setprofile(traceit.traceit)
"""
import sys
WHITE_LIST = {'trade'}      # Look for these words in the file path.
EXCLUSIONS = {'<'}          # Ignore <listcomp>, etc. in the function name.
def tracefunc(frame, event, arg):
    if event == "call":
        tracefunc.stack_level += 1
        unique_id = frame.f_code.co_filename+str(frame.f_lineno)
        if unique_id in tracefunc.memorized:
            return
        # Part of filename MUST be in white list.
        if any(x in frame.f_code.co_filename for x in WHITE_LIST) \
            and \
          not any(x in frame.f_code.co_name for x in EXCLUSIONS):
            if 'self' in frame.f_locals:
                class_name = frame.f_locals['self'].__class__.__name__
                func_name = class_name + '.' + frame.f_code.co_name
            else:
                func_name = frame.f_code.co_name
            func_name = '{name:->{indent}s}()'.format(
                    indent=tracefunc.stack_level*2, name=func_name)
            txt = '{: <40} # {}, {}'.format(
                    func_name, frame.f_code.co_filename, frame.f_lineno)
            print(txt)
            tracefunc.memorized.add(unique_id)
    elif event == "return":
        tracefunc.stack_level -= 1
tracefunc.memorized = set()
tracefunc.stack_level = 0

示例用法:

import traceit
sys.setprofile(traceit.tracefunc)

示例输出:

API.getFills()                           # C:\Python37-32\lib\site-packages\helpers\trade\tws3.py, 331
API._get_req_id()                        # C:\Python37-32\lib\site-packages\helpers\trade\tws3.py, 1053
API._wait_till_done()                    # C:\Python37-32\lib\site-packages\helpers\trade\tws3.py, 1026
---API.execDetails()                     # C:\Python37-32\lib\site-packages\helpers\trade\tws3.py, 1187
-------Fill.__init__()                   # C:\Python37-32\lib\site-packages\helpers\trade\mdb.py, 256
--------Price.__init__()                 # C:\Python37-32\lib\site-packages\helpers\trade\mdb.py, 237
-deserialize_order_ref()                 # C:\Python37-32\lib\site-packages\helpers\trade\mdb.py, 644
--------------------Port()               # C:\Python37-32\lib\site-packages\helpers\trade\mdb.py, 647
API.commissionReport()                   # C:\Python37-32\lib\site-packages\helpers\trade\tws3.py, 1118

特点:

- 忽略Python语言内部函数。

- 忽略重复的函数调用(可选)。

- 为了提高速度,使用sys.setprofile()方法而不是sys.settrace()方法。

有15行破折号打印出每个函数的信息

traceit是什么?它没有被定义,也不是一个模块

与其优化代码,不如先解决traceit未定义的问题吗?

我编辑了答案。希望现在更清楚了。

是否有这段代码的使用示例?当我尝试使用简单的foo/bar脚本时,它似乎没有任何作用。

0
0 Comments

问题出现的原因是,作者想要在函数被调用时打印出函数的名称。然而,目前作者只找到了使用trace模块的三种选项来显示函数名称和执行的代码行数。

解决方法是使用trace模块的不同选项来达到目的。第一种方法是使用`-l/--listfuncs`选项来列出函数名称。第二种方法是使用`-t/--trace`选项来按执行的代码行数列出函数和代码行数。第三种方法是使用`-T/--trackcalls`选项来列出函数之间的调用关系。

通过这些方法,作者可以打印出函数名称和代码行数,以满足他的需求。

0
0 Comments

问题:如何在函数被调用时打印函数?

原因:想要在函数被调用时打印函数的名称和返回值。

解决方法:使用跟踪函数来实现这一功能。可以通过以下代码实现:

def tracefunc(frame, event, arg, indent=[0]):
    if event == "call":
        indent[0] += 2
        print("-" * indent[0] + "> call function", frame.f_code.co_name)
    elif event == "return":
        print("<" + "-" * indent[0], "exit function", frame.f_code.co_name)
        indent[0] -= 2
    return tracefunc
import sys
sys.setprofile(tracefunc)
main()   # or whatever kicks off your script

这个跟踪函数会在函数被调用时打印出函数的名称,并在函数返回时打印出函数的退出信息。

需要注意的是,函数的代码对象通常与关联的函数具有相同的名称,但并不总是如此,因为函数可以动态创建。不幸的是,Python不会跟踪堆栈上的函数对象。如果遇到这个问题,可以从源代码中提取“真实”的函数名称,Python会跟踪文件名和行号,或者可以通过垃圾回收器找出哪个函数对象引用了代码对象。可能会有多个函数共享代码对象,但其中任何一个函数的名称都足够使用。

可以使用全局变量来跟踪嵌套级别,并使跟踪结果更加美观。

在Python 2.6及更高版本中,可以使用`sys.setprofile()`而不是`sys.settrace()`来获得更好的性能。这两个函数使用相同的跟踪函数,只是`sys.setprofile()`只在函数进入或退出时调用跟踪函数,所以函数内部的代码会以全速执行。

还可以在跟踪函数中添加代码来打印调用函数所在的文件。

如果`event`是`'c_call'`,可以使用`frame.f_code.co_name`来获取函数名称。

如果只想打印自己代码中定义的函数,而不是所有Python内部函数,可以检查`frame.f_code.co_filename`。这个属性应该是包含函数的文件的完整路径。可以检查路径中是否包含`Python`和`lib`,如果包含,则不打印任何内容。

如果想在获取方法时查看类,可以尝试使用`if "self" in frame.f_locals: print(type(frame.f_locals["self"]).__qualname__)`。

如果想在函数被调用时打印返回值,可以使用装饰器实现,但不能作为跟踪函数的一部分。

0