如何在调用函数时打印它们

Jam*_*mes 60 python callstack

在调试Python脚本时,我真的想知道整个程序的整个调用堆栈.一个理想的情况是,如果有一个python的命令行标志会导致Python在调用它们时打印所有函数名称(我检查过man Python2.7,但没有找到这种类型的任何东西).

由于此脚本中的函数数量,如果可能,我不希望在每个函数和/或类的开头添加print语句.

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

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

kin*_*all 94

您可以使用跟踪功能(对Spacedman提出道具以改进其原始版本以跟踪返回并使用一些不错的缩进):

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.settrace(tracefunc)

main()   # or whatever kicks off your script
Run Code Online (Sandbox Code Playgroud)

请注意,函数的代码对象通常与关联函数具有相同的名称,但并非总是如此,因为可以动态创建函数.不幸的是,Python没有跟踪堆栈中的函数对象(我有时会幻想为此提交补丁).不过,在大多数情况下,这肯定"足够好".

如果这成为问题,您可以从源代码中提取"真实"函数名称 - Python会跟踪文件名和行号 - 或者要求垃圾收集器找出哪个函数对象引用代码对象.可能有多个函数共享代码对象,但它们的任何名称都可能足够好.

回过头来重新审视这个四年之后,我有理由提到在Python 2.6及更高版本中,你可以通过使用sys.setprofile()而不是使用而获得更好的性能sys.settrace().可以使用相同的跟踪功能; 只是在输入或退出函数时才调用配置文件功能,因此函数内部全速执行.

  • 这太棒了.我最终将`os.path.basename(frame.f_code.co_filename)`添加到此跟踪函数中以打印包含所调用函数的文件. (4认同)
  • 你可以检查`frame.f_code.co_filename`。这应该是包含该函数的文件的完整路径。检查路径是否包含`Python`后跟`lib`,也许,如果是,不要打印任何东西...... (2认同)
  • @Dirk:似乎你可以简单地使用“frame.f_code.co_filename”来检查该函数是否在一个(或多个)源文件中,否则忽略它——而不是检查它是否是Python内部的。 (2认同)

Dav*_*ver 12

另一个需要注意的好工具是跟踪模块:

$ cat foo.py
def foo():
   bar()

def bar():
   print "in bar!"

foo()

$ python -m trace --listfuncs foo.py
in bar!

functions called:
filename: /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/trace.py, modulename: trace, funcname: _unsettrace
filename: foo.py, modulename: foo, funcname: 
filename: foo.py, modulename: foo, funcname: bar
filename: foo.py, modulename: foo, funcname: foo

$python -m trace --trace foo.py
 --- modulename: foo, funcname: 
foo.py(1): def foo():
foo.py(4): def bar():
foo.py(7): foo()
 --- modulename: foo, funcname: foo
foo.py(2):    bar()
 --- modulename: foo, funcname: bar
foo.py(5):    print "in bar!"
in bar!
 --- modulename: trace, funcname: _unsettrace
trace.py(80):         sys.settrace(None)

Run Code Online (Sandbox Code Playgroud)

  • `trace` 很有用,但我找不到如何生成 OP 请求的输出:`-l` 仅显示每个函数一次,`-t` 显示每一行。 (2认同)

Cha*_*imG 8

我接受了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
Run Code Online (Sandbox Code Playgroud)

示例用法

import traceit

sys.setprofile(traceit.tracefunc)
Run Code Online (Sandbox Code Playgroud)

示例输出:

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
Run Code Online (Sandbox Code Playgroud)

特征:

  • 忽略 Python 语言内部函数。
  • 忽略重复的函数调用(可选)。
  • 使用 sys.setprofile() 而不是 sys.settrace() 来提高速度。

  • 什么是traceit?它没有定义,也不是一个模块 (3认同)

Sve*_*ach 7

有几个选择.如果一个调试器是不够的,你可以设置一个跟踪功能使用sys.settrace().这个函数基本上会在每行执行的Python代码上调用,但很容易识别函数调用 - 请参阅链接文档.

您可能也对该trace模块感兴趣,但它并不完全符合您的要求.一定要查看--trackcalls选项.


Chr*_*unt 7

hunter工具正是这样做的,甚至更多。例如,给定:

测试.py

def foo(x):
    print(f'foo({x})')

def bar(x):
    foo(x)

bar()
Run Code Online (Sandbox Code Playgroud)

输出看起来像:

$ PYTHONHUNTER='module="__main__"' python test.py
                                 test.py:1     call      => <module>()
                                 test.py:1     line         def foo(x):
                                 test.py:4     line         def bar(x):
                                 test.py:7     line         bar('abc')
                                 test.py:4     call         => bar(x='abc')
                                 test.py:5     line            foo(x)
                                 test.py:1     call            => foo(x='abc')
                                 test.py:2     line               print(f'foo({x})')
foo(abc)
                                 test.py:2     return          <= foo: None
                                 test.py:5     return       <= bar: None
                                 test.py:7     return    <= <module>: None
Run Code Online (Sandbox Code Playgroud)

它还提供了非常灵活的查询语法,允许指定模块、文件/行号、函数等,这很有帮助,因为默认输出(包括标准库函数调用)可能非常大。


Abh*_*jit 5

import traceback
def foo():
    traceback.print_stack()
def bar():
    foo()
def car():
    bar():

car()
File "<string>", line 1, in <module>
File "C:\Python27\lib\idlelib\run.py", line 97, in main
  ret = method(*args, **kwargs)
File "C:\Python27\lib\idlelib\run.py", line 298, in runcode
    exec code in self.locals
File "<pyshell#494>", line 1, in <module>
File "<pyshell#493>", line 2, in car
File "<pyshell#490>", line 2, in bar
File "<pyshell#486>", line 2, in foo
Run Code Online (Sandbox Code Playgroud)

追溯