python函数实验报告的简单介绍( 七 )


在深入讨论profiling之前 , 注意一些易于学习和使用的Python程序设计习惯是有意义的,并且对提高程序性能不无裨益 。这些技术都不是特定于某个Python版本的,而是合理的Python程序设计风格 。第一,在需要只读序列时,最好使用元组而非列表; 第二,使用生成器,而不是创建大的元组和列表并在其上进行迭代处理;第三,尽量使用Python内置的数据结构 dicts、lists、tuples 而不实现自己的自定义结构,因为内置的数据结构都是经过了高度优化的;第四,从小字符串中产生大字符串时, 不要对小字符串进行连接,而是在列表中累积,最后将字符串列表结合成为一个单独的字符串;第五,也是最后一点,如果某个对象(包括函数或方法)需要多次使用属性进行访问(比如访问模块中的某个函数) , 或从某个数据结构中进行访问 , 那么较好的做法是创建并使用一个局部变量来访问该对象,以便提供更快的访问速度 。
Python标准库提供了两个特别有用的模块,可以辅助调査代码的性能问题 。一个是timeit模块——该模块可用于对一小段Python代码进行计时,并可用于诸如对两个或多个特定函数或方法的性能进行比较等场合 。另一个是cProfile模块,可用于profile 程序的性能——该模块对调用计数与次数进行了详细分解,以便发现性能瓶颈所在 。
为了解timeit模块,我们将查看一些小实例 。假定有3个函数function_a()、 function_b()、function_c(), 3个函数执行同样的计算,但分别使用不同的算法 。如果将这些函数放于同一个模块中(或分别导入),就可以使用timeit模块对其进行运行和比较 。下面给出的是模块最后使用的代码:
if __name__ == "__main__":
repeats = 1000
for function in ("function_a", "function_b", "function_c"):
t = timeit.Timer("{0}(X, Y)".format(function),"from __main__ import {0}, X, Y".format(function))
sec = t.timeit(repeats) / repeats
print("{function}() {sec:.6f} sec".format(**locals()))
赋予timeit.Timer()构造子的第一个参数是我们想要执行并计时的代码 , 其形式是字符串 。这里,该字符串是“function_a(X,Y)”;第二个参数是可选的,还是一个待执行的字符串,这一次是在待计时的代码之前,以便提供一些建立工作 。这里,我们从 __main__ (即this)模块导入了待测试的函数,还有两个作为输入数据传入的变量(X 与Y),这两个变量在该模块中是作为全局变量提供的 。我们也可以很轻易地像从其他模块中导入数据一样来进行导入操作 。
调用timeit.Timer对象的timeit()方法时,首先将执行构造子的第二个参数(如果有) ,  之后执行构造子的第一个参数并对其执行时间进行计时 。timeit.Timer.timeit()方法的返回值是以秒计数的时间,类型是float 。默认情况下,timeit()方法重复100万次,并返回所 有这些执行的总秒数,但在这一特定案例中,只需要1000次反复就可以给出有用的结果, 因此对重复计数次数进行了显式指定 。在对每个函数进行计时后,使用重复次数对总数进行除法操作,就得到了平均执行时间,并在控制台中打印出函数名与执行时间 。
function_a() 0.001618 sec
function_b() 0.012786 sec
function_c() 0.003248 sec
在这一实例中,function_a()显然是最快的——至少对于这里使用的输入数据而言 。在有些情况下一一比如输入数据不同会对性能产生巨大影响——可能需要使用多组输入数据对每个函数进行测试 , 以便覆盖有代表性的测试用例 , 并对总执行时间或平均执行时间进行比较 。
有时监控自己的代码进行计时并不是很方便 , 因此timeit模块提供了一种在命令行中对代码执行时间进行计时的途径 。比如,要对MyModule.py模块中的函数function_a()进行计时,可以在控制台中输入如下命令:python3 -m timeit -n 1000 -s "from MyModule import function_a, X, Y" "function_a(X, Y)"(与通常所做的一样,对 Windows 环境,我们必须使用类似于C:Python3lpython.exe这样的内容来替换python3) 。-m选项用于Python 解释器 , 使其可以加载指定的模块(这里是timeit),其他选项则由timeit模块进行处理 。-n选项指定了循环计数次数 , -s选项指定了要建立 , 最后一个参数是要执行和计时的代码 。命令完成后 , 会向控制台中打印运行结果 , 比如:

推荐阅读