如何打印调用的函数?

发布于 2024-12-19 03:51:09 字数 301 浏览 2 评论 0原文

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

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

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

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

In debugging a Python script, I'd really like to know the entire call stack for my entire program. An ideal situation would be if there were a command-line flag for python that would cause Python to print all function names as they are called (I checked man Python2.7, but didn't find anything of this sort).

Because of the number of functions in this script, I'd prefer not to add a print statement to the beginning of each function and/or class, if possible.

An intermediate solution would be to use PyDev's debugger, place a couple breakpoints and check the call stack for given points in my program, so I'll use this approach for the time being.

I'd still prefer to see a complete list of all functions called throughout the life of the program, if such a method exists.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

如痴如狂 2024-12-26 03:51:09

您可以使用跟踪函数来做到这一点(支持 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.setprofile(tracefunc)

main()   # or whatever kicks off your script

请注意,函数的代码对象通常与关联函数具有相同的名称,但并非总是如此,因为可以动态创建函数。不幸的是,Python 不跟踪堆栈上的函数对象(我有时幻想为此提交一个补丁)。尽管如此,在大多数情况下这确实“足够好”。

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

四年后再次回顾这一点时,我有必要提到,在 Python 2.6 及更高版本中,您可以通过使用 sys.setprofile() 而不是 获得更好的性能>sys.settrace()。可以使用相同的跟踪功能;只是只有在进入或退出函数时才会调用配置文件函数,因此函数内部的内容会全速执行。

You can do this with a trace function (props to Spacedman for improving the original version of this to trace returns and use some nice indenting):

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

Note that a function's code object usually has the same name as the associated function, but not always, since functions can be created dynamically. Unfortunately, Python doesn't track the function objects on the stack (I've sometimes fantasized about submitting a patch for this). Still, this is certainly "good enough" in most cases.

If this becomes an issue, you could extract the "real" function name from the source code—Python does track the filename and line number—or ask the garbage collector find out which function object refers to the code object. There could be more than one function sharing the code object, but any of their names might be good enough.

Coming back to revisit this four years later, it behooves me to mention that in Python 2.6 and later, you can get better performance by using sys.setprofile() rather than sys.settrace(). The same trace function can be used; it's just that the profile function is called only when a function is entered or exited, so what's inside the function executes at full speed.

梦断已成空 2024-12-26 03:51:09

另一个需要注意的好工具是 trace 模块。显示函数名称有 3 个选项。

示例 foo.py

def foo():
   bar()

def bar():
   print("in bar!")

foo()
  1. 使用 -l/--listfuncs列出函数
$ 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: <module>
filename: foo.py, modulename: foo, funcname: bar
filename: foo.py, modulename: foo, funcname: foo
  1. 使用 -t/--trace > 列出执行的行
$python -m trace --trace foo.py
 --- modulename: foo, funcname: <module>
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!
  1. 使用 -T/--trackcalls列出什么调用什么
$ python -m trace --trackcalls foo.py
in bar!

calling relationships:

*** /usr/lib/python3.8/trace.py ***
  --> foo.py
    trace.Trace.runctx -> foo.<module>

*** foo.py ***
    foo.<module> -> foo.foo
    foo.foo -> foo.bar

Another good tool to be aware of is the trace module. There are 3 options of showing function names.

Example foo.py:

def foo():
   bar()

def bar():
   print("in bar!")

foo()
  1. Using -l/--listfuncs to list funtions:
$ 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: <module>
filename: foo.py, modulename: foo, funcname: bar
filename: foo.py, modulename: foo, funcname: foo
  1. Using -t/--trace to list lines as they are executed.
$python -m trace --trace foo.py
 --- modulename: foo, funcname: <module>
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!
  1. Using -T/--trackcalls to list what calls what
$ python -m trace --trackcalls foo.py
in bar!

calling relationships:

*** /usr/lib/python3.8/trace.py ***
  --> foo.py
    trace.Trace.runctx -> foo.<module>

*** foo.py ***
    foo.<module> -> foo.foo
    foo.foo -> foo.bar
夜未央樱花落 2024-12-26 03:51:09

我接受了kindall的答案并以此为基础。我制作了以下模块:

"""traceit.py

Traces the call stack.

Usage:

import sys
import traceit

sys.setprofile(traceit.tracefunc)
"""

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() 来提高速度。

I took kindall's answer and built on it. I made the following module:

"""traceit.py

Traces the call stack.

Usage:

import sys
import traceit

sys.setprofile(traceit.tracefunc)
"""

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

Sample usage

import traceit

sys.setprofile(traceit.tracefunc)

Sample output:

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

Features:

  • Ignores Python language internal functions.
  • Ignores repeated function calls (optional).
  • Uses sys.setprofile() instead of sys.settrace() for speed.
别念他 2024-12-26 03:51:09

有几种选择。如果调试器还不够,您可以使用 sys.settrace()。该函数本质上会在执行的每一行 Python 代码上被调用,但很容易识别函数调用——请参阅链接的文档。

您可能还对 trace 模块感兴趣,尽管它并没有完全按照你的要求做。请务必查看 --trackcalls 选项。

There are a few options. If a debugger isn't enough, you can set a trace function using sys.settrace(). This function will be essentially called on every line of Python code executed, but it easy to identify the function calls -- see the linked documentation.

You might also be interested in the trace module, though it doesn't do exactly what you asked for. Be sure to look into the --trackcalls option.

长途伴 2024-12-26 03:51:09
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

回溯

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

traceback

飘过的浮云 2024-12-26 03:51:09

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

test.py

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

def bar(x):
    foo(x)

bar()

输出如下所示:

$ 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

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

The hunter tool does exactly this, and more. For example, given:

test.py:

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

def bar(x):
    foo(x)

bar()

The output looks like:

$ 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

It also provides a pretty flexible query syntax that allows specifying module, file/lineno, function, etc which helps because the default output (which includes standard library function calls) can be pretty big.

淡写薰衣草的香 2024-12-26 03:51:09

You could use settrace, as outlined here: Tracing python code. Use the version near the end of the page. I stick the code of that page into my code to see exactly what lines are executed when my code is running. You can also filter so that you only see the names of functions called.

妳是的陽光 2024-12-26 03:51:09

您还可以对要跟踪的特定函数(及其参数)使用装饰器:

import sys
from functools import wraps

class TraceCalls(object):
    """ Use as a decorator on functions that should be traced. Several
        functions can be decorated - they will all be indented according
        to their call depth.
    """
    def __init__(self, stream=sys.stdout, indent_step=2, show_ret=False):
        self.stream = stream
        self.indent_step = indent_step
        self.show_ret = show_ret

        # This is a class attribute since we want to share the indentation
        # level between different traced functions, in case they call
        # each other.
        TraceCalls.cur_indent = 0

    def __call__(self, fn):
        @wraps(fn)
        def wrapper(*args, **kwargs):
            indent = ' ' * TraceCalls.cur_indent
            argstr = ', '.join(
                [repr(a) for a in args] +
                ["%s=%s" % (a, repr(b)) for a, b in kwargs.items()])
            self.stream.write('%s%s(%s)\n' % (indent, fn.__name__, argstr))

            TraceCalls.cur_indent += self.indent_step
            ret = fn(*args, **kwargs)
            TraceCalls.cur_indent -= self.indent_step

            if self.show_ret:
                self.stream.write('%s--> %s\n' % (indent, ret))
            return ret
        return wrapper

只需导入此文件并在要跟踪的函数/方法之前添加 @TraceCalls() 即可。

You can also use a decorator for specific functions you want to trace (with their arguments):

import sys
from functools import wraps

class TraceCalls(object):
    """ Use as a decorator on functions that should be traced. Several
        functions can be decorated - they will all be indented according
        to their call depth.
    """
    def __init__(self, stream=sys.stdout, indent_step=2, show_ret=False):
        self.stream = stream
        self.indent_step = indent_step
        self.show_ret = show_ret

        # This is a class attribute since we want to share the indentation
        # level between different traced functions, in case they call
        # each other.
        TraceCalls.cur_indent = 0

    def __call__(self, fn):
        @wraps(fn)
        def wrapper(*args, **kwargs):
            indent = ' ' * TraceCalls.cur_indent
            argstr = ', '.join(
                [repr(a) for a in args] +
                ["%s=%s" % (a, repr(b)) for a, b in kwargs.items()])
            self.stream.write('%s%s(%s)\n' % (indent, fn.__name__, argstr))

            TraceCalls.cur_indent += self.indent_step
            ret = fn(*args, **kwargs)
            TraceCalls.cur_indent -= self.indent_step

            if self.show_ret:
                self.stream.write('%s--> %s\n' % (indent, ret))
            return ret
        return wrapper

Just import this file and add a @TraceCalls() before the function/method you want to trace.

已下线请稍等 2024-12-26 03:51:09

kindall 答案的变体,仅返回包中被调用的函数。

def tracefunc(frame, event, arg, indent=[0]):
    package_name = __name__.split('.')[0]

    if event == "call" and (package_name in str(frame)):
        indent[0] += 2
        print("-" * indent[0] + "> call function", frame.f_code.co_name)
    return tracefunc

import sys
sys.settrace(tracefunc)

例如,在名为 Dog 的包中,这应该只显示在 Dog 包中定义的调用函数。

Variation on kindall's answer, return just the called functions in a package.

def tracefunc(frame, event, arg, indent=[0]):
    package_name = __name__.split('.')[0]

    if event == "call" and (package_name in str(frame)):
        indent[0] += 2
        print("-" * indent[0] + "> call function", frame.f_code.co_name)
    return tracefunc

import sys
sys.settrace(tracefunc)

e.g. In a package called Dog, this should only show you functions called that were defined in the Dog package.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文