当我使用cProfile分析我正在开发的程序时,我发现我花了很多时间进行昂贵的
是否有一种工具可以让我标记函数调用,并通知我哪些函数在Python中调用了该函数?
zip
调用。我编写的代码并没有进行这些zip调用,所以它必须在我导入的许多库之一中的代码中。是否有一种工具可以让我标记函数调用,并通知我哪些函数在Python中调用了该函数?
您可以从分析器输出中获取这些信息。从输出中创建一个Stats对象,然后调用stats.print_callers('zip')
。
这将显示调用它的函数,对于每个调用者,它被调用的次数以及在调用中花费的总时间和累计时间。
我之前也遇到过类似的问题,并且成功解决了。虽然这不是一个工具,但或许对您有用。只需要将以下代码放在主模块的顶部即可。
注意: 该代码是为你所需的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)
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
和“罪魁祸首”函数的调用次数应该有某种比例关系。
当然,可能会有很多噪音掩盖这些关系,但你应该努力找出来。
cProfile
的输出已经在函数的累计时间成本中“隐藏”了那些信息。提供cProfile
的输出将会很有帮助。 - Bakuriu