python中的行分析类实例化

nau*_*101 2 python inheritance instantiation line-profiler

我有一些我正在尝试分析的现有代码。我可以通过@profile使用kernprof添加装饰器来成功地排列配置文件类方法。

有没有一种通用的方法来分析类实例化?我有几个类具有非常复杂的继承结构。当我尝试分析它们的 init 函数时,我得到如下信息:

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
   179                                               def __init__(self, data):
   180         1    8910739.0 8910739.0    100.0          super().__init__(data)
   181         1         10.0      10.0      0.0          self.mortgage_rate = 5.2  # rate in percentage
Run Code Online (Sandbox Code Playgroud)

这有点没用,因为我不知道__init__正在调用什么实际的父函数(这个类有 2 个父级,每个父级都有一个或多个父级)。

有什么办法可以做得更好吗?例如,有没有办法自动潜入每条线,并分析它调用的线(深度有限)?

gdl*_*lmx 5

有很多方法:

trace模块

标准 python 库中的trace模块提供了一个方便的函数来逐行跟踪程序的执行。因此,很容易确定您的__init__方法调用了哪个函数。

尝试在 python shell 中运行以下代码

from MyMod import MyClass
# Do necessary preparation for your module HERE

# --- Setup and start tracing ---
import sys, trace
tracer = trace.Trace( trace=0, count=0, timing=True,  countcallers=True)
tracer.run('MyClass()') # init your class and track the function calls
tracer.results().write_results(show_missing=False) # print result to the screen
Run Code Online (Sandbox Code Playgroud)

跟踪器将显示通过运行程序暴露的调用关系。

MyDependency.Fourth.__init__ -> MyDependency.Second.__init__
MyDependency.Second.__init__ -> MyDependency.Third.__init__
MyDependency.Third.__init__ -> MyDependency.First.__init__
MyClass.Child.__init__ -> MyDependency.Fourth.__init__
Run Code Online (Sandbox Code Playgroud)

trace模块还有一个 CLI。上面的python代码相当于这个shell命令:

python -m trace -T test.py | grep __init__
Run Code Online (Sandbox Code Playgroud)

其中 option-T相当于countcallers=True. 目标脚本test.py应该包含最少的代码来初始化您的类。

将 line-profiler 添加到调用的函数

现在您知道在类初始化中调用的模块、类和方法的名称。然后你可以@profile为这些函数添加装饰器。附带说明:不需要修改每个模块的源代码来添加装饰器。只需将它们导入您的主模块并运行即可 profile.add_function(MyDependency.Third.__init__) 获得相同的效果。

如果要获得所有调用的 Python 代码行的时间顺序跟踪,请使用以下选项

tracer = trace.Trace( ignoredirs=[sys.prefix, sys.exec_prefix ], trace=1, count=0, timing=True )
Run Code Online (Sandbox Code Playgroud)

它会打印出来

 --- modulename: MyMod, funcname: __init__
0.00 MyMod.py(6):         super().__init__()
 --- modulename: MyDependency, funcname: __init__
0.00 MyDependency.py(17):         super().__init__()
...
Run Code Online (Sandbox Code Playgroud)

其中第一列是步行时钟时间。

sys.setprofile方法

您可以通过该sys.setprofile方法注册回调函数。它将接收堆栈转换事件(当函数被调用或返回时)。每个事件都带有一个堆栈帧对象,您可以从中记录模块、类和调用的函数。

这种方法会给你最大的灵活性。例如,您可以使用堆栈深度或执行时间长度过滤掉函数调用。有关用法示例,请参阅我的旧帖子以了解类似问题。

上述示例的文件结构

以上结果基于以下取自另一篇文章的模块/类结构。

文件“MyDependency.py”

class First:
    ...
class Second(First):
    ...
class Third(First):
    ...
class Fourth(Second, Third):
    ...
Run Code Online (Sandbox Code Playgroud)

文件“MyModel.py”

from MyDependency import Fourth
class MyClass(Fourth):
    def __init__(self):
        super().__init__()
Run Code Online (Sandbox Code Playgroud)