Python七号Python

Python调试和性能分析的法宝

2019-07-22  本文已影响47人  somenzz

学 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核心技术与实践》,想购买的话可以先看下我之前的文章,文末有返现活动 :
感受一下大神的力量

(完)

上一篇 下一篇

猜你喜欢

热点阅读