美文网首页Python七号Python
Python调试和性能分析的法宝

Python调试和性能分析的法宝

作者: somenzz | 来源:发表于2019-07-22 17:24 被阅读47次

    学 Python 的你不可能没有听说过一些编辑工具,如 Pycharm,VScode 等等,他们除了提供对代码的格式化,注释,跳转等便捷处理方式之外,还提供 debug 功能,不可谓不强大。

    假如我们的测试环境是 AIX 操作系统的,或者是一些 Linux 系统上,但是没有也不能安装 Pycharm,VScode工具,那我们该如何调试,或者做性能分析叫?

    这就是今天推荐的两大法宝:pdb 和 cProfile

    一、调试工具 pdb

    Python 的 pdb,是其自带的一个调试库。它为 Python 程序提供了交互式的源代码调试功能,是命令行版本的 IDE 断点调试器,完美地解决了不借助工具进行调试的问题。

    a = 1
    b = 2
    import pdb
    pdb.set_trace()
    c = 3
    print(a + b + c)
    
    

    当我们运行这个程序时时,它的输出界面是下面这样的,表示程序已经运行到了

    > /Users/jingxiao/test.py(5)<module>()
    -> c = 3
    

    此时表示代码即将准备运行 c = 3。

    如果要执行下一句,直接输入 “n” 回车。
    如果要打印变量名,则输入 “p 变量名” 再回车。
    如果要跳转到函数内部执行,则输入 “s” 再回车,就是 step into 的意思。
    如果要跳出函数内部,则输入"r" 再回车,就是 stop out 的意思。
    如果要在某行设置断点,则输入 “b 断点行号” 再回车。
    如果要一直运行到下一个断点,则输入 “c” 再回车。

    更多使用帮助,请参考官方文档:

    https://docs.python.org/3/library/pdb.html#module-pdb

    二、性能分析工具 cProfile

    Python 自带的 cprofile 库,可以对代码的每个部分进行动态的分析,比如准确计算出每个模块消耗的时间等。这样你就可以知道程序的瓶颈所在,从而对其进行修正或优化。当然,这并不需要你花费特别大的力气。

    举个例子,以下代码如何分析哪一步耗时多?

    def fib(n):
        if n == 0:
            return 0
        elif n == 1:
            return 1
        else:
            return fib(n-1) + fib(n-2)
    
    def fib_seq(n):
        res = []
        if n > 0:
            res.extend(fib_seq(n-1))
        res.append(fib(n))
        return res
    
    print(fib_seq(30))
    

    一种方法是这样运行:

    import cProfile
    # def fib(n)
    # def fib_seq(n):
    cProfile.run('fib_seq(30)')
    
    

    另一种方法是这样运行:

    python -m cProfile test.py
    

    运行结果如下所示:

    ncalls,是指相应代码 / 函数被调用的次数;

    tottime,是指对应代码 / 函数总共执行所需要的时间(注意,并不包括它调用的其他代码 / 函数的执行时间);

    percall,就是上述两者相除的结果,也就是 tottime / ncalls;

    cumtime,则是指对应代码 / 函数总共执行所需要的时间,这里包括了它调用的其他代码 / 函数的执行时间;

    cumtime percall,则是 cumtime 和 ncalls 相除的平均结果。

    了解这些参数后,再来看这张图。我们可以清晰地看到,这段程序执行效率的瓶颈,在于函数 fib(),它被调用了 700 多万次。

    有没有什么办法可以提高改进呢?答案是肯定的。通过观察,我们发现,程序中有很多对 fib() 的调用,其实是重复的,那我们就可以用字典来保存计算过的结果,防止重复。改进后的代码如下所示:

    def memoize(f):
        memo = {}
        def helper(x):
            if x not in memo:            
                memo[x] = f(x)
            return memo[x]
        return helper
    
    @memoize
    def fib(n):
        if n == 0:
            return 0
        elif n == 1:
            return 1
        else:
            return fib(n-1) + fib(n-2)
    
    
    def fib_seq(n):
        res = []
        if n > 0:
            res.extend(fib_seq(n-1))
        res.append(fib(n))
        return res
    
    fib_seq(30)
    
    

    再次执行 cProfile 得到下面的结果:

    可以看到性能有极大的提升。

    这个简单的例子,便是 cProfile 的基本用法。当然,cProfile 还有很多其他功能,还可以结合 stats 类来使用,你可以阅读相应的官方文档。

    三、购买 Python 课程

    内容部分摘录自极客专栏《Python核心技术与实践》,想购买的话可以先看下我之前的文章,文末有返现活动 :
    感受一下大神的力量

    (完)

    相关文章

      网友评论

        本文标题:Python调试和性能分析的法宝

        本文链接:https://www.haomeiwen.com/subject/iqpwlctx.html