追踪Python中内置函数的调用位置

3
当我使用cProfile分析我正在开发的程序时,我发现我花了很多时间进行昂贵的zip调用。我编写的代码并没有进行这些zip调用,所以它必须在我导入的许多库之一中的代码中。
是否有一种工具可以让我标记函数调用,并通知我哪些函数在Python中调用了该函数?

1
我相信 cProfile 的输出已经在函数的累计时间成本中“隐藏”了那些信息。提供 cProfile 的输出将会很有帮助。 - Bakuriu
3个回答

4

您可以从分析器输出中获取这些信息。从输出中创建一个Stats对象,然后调用stats.print_callers('zip')

这将显示调用它的函数,对于每个调用者,它被调用的次数以及在调用中花费的总时间和累计时间。


0

我之前也遇到过类似的问题,并且成功解决了。虽然这不是一个工具,但或许对您有用。只需要将以下代码放在主模块的顶部即可。

注意: 该代码是为你所需的zip函数量身定制的,在我的应用中它有效(当然,对于另一个函数,我现在忘记了名称)。我做的额外导入是来自第三方模块,我不知道是否适用于标准库中的模块。

import inspect

def trackZip(*args):
    print inspect.getouterframes(inspect.currentframe())[1]
    return __builtins__.zip(*args)

zip = trackZip

#do more imports...

def test():
    zip([1,2],[3,4])

test()
zip([1,2],[3,4])

输出:

(<frame object at 0x1948260>, '/home/user/untitled4.py', 17, 'test', ['    zip([1,2],[3,4])\n'], 0)
(<frame object at 0x11fd3d0>, '/home/user/untitled4.py', 20, '<module>', ['zip([1,2],[3,4])\n'], 0)

0
一个快速而简单的实现你想要的方法是:用自定义函数替换内置的zip!
In [8]: import inspect
In [9]: def my_zip(*iterables):
   ...:     frame = inspect.currentframe().f_back
   ...:     my_zip.callees.append(frame.f_code.co_name)
   ...:     return my_zip.old_zip(*iterables)

In [10]: my_zip.callees = []

In [11]: my_zip.old_zip = zip

In [12]: import builtins

In [13]: builtins.zip = my_zip

In [14]: zip(range(5), range(4))
Out[14]: <builtins.zip at 0x7f06a2324290>

In [15]: zip.callees  # called at module level...
Out[15]: ['<module>']

(在Python2中,将 builtins 模块替换为 __builtin__ )。

更智能的实现将使用 collections.Counter ,并避免保留调用 zip 的所有函数名称列表。

但是,您可能需要了解调用者 zip 的名称以上的信息。也许有另一个函数 f 调用 g 再调用 h 最终调用 zip ,但要确定 f 是问题的真正原因,必须以某种方式将 h f 关联起来。

不过,扩展 my_zip 以跟踪更多信息应该不难。


然而,我认为cProfile的输出应该提供足够的时间信息来更或多或少地理解zip的调用位置。尤其是你应该仔细研究其输出中的cumtime列。

你还应考虑到函数的调用次数。通常情况下,一个函数在被调用时总会执行相同数量的zip操作,因此对于zip和“罪魁祸首”函数的调用次数应该有某种比例关系。

当然,可能会有很多噪音掩盖这些关系,但你应该努力找出来。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接