盘点提高 Python 代码效率的方法


Posted in Python onJuly 03, 2014

第一招:蛇打七寸:定位瓶颈

首先,第一步是定位瓶颈。举个简单的栗子,一个函数可以从1秒优化到到0.9秒,另一个函数可以从1分钟优化到30秒,如果要花的代价相同,而且时间限制只能搞定一个,搞哪个?根据短板原理,当然选第二个啦。

一个有经验的程序员在这里一定会迟疑一下,等等?函数?这么说,还要考虑调用次数?如果第一个函数在整个程序中需要被调用100000次,第二个函数在整个程序中被调用1次,这个就不一定了。举这个栗子,是想说明,程序的瓶颈有的时候不一定一眼能看出来。还是上面那个选择,程序员的你应该有感觉的,大多数情况下:一个「可以」从一分钟优化到30秒的函数会比一个「可以」从1秒优化到0.9秒的函数更容易捕获我们的注意,因为有很大的进步空间嘛。

所以,这么多废话讲完,献上第一招,profile。这是 python 自带的定位程序瓶颈的利器!虽然它提供了三种选项profile,cProfile,hotshot。还分为内置和外置。但是,个人觉得一种足矣,外置cProfile。心法如下:

python -m profile 逗比程序.py

这招的效果会输出一系列东西,比如函数被调用了几次,总时间多少,其中有多少是这个函数的子函数花费的,每次花多少时间,等等。嘛一图胜千言:

filename:lineno(function): 文件名:第几行(函数名)
ncalls: 这货一共调用了几次
tottime: 这货自己总共花了多少时间,也就是要除掉内部函数小弟们的花费
percall: 平均每次调用花的时间,tottime 除以 ncalls
cumtime: 这货还有它的所有内部函数小弟们的总花费
percall: 跟上面那个 percall 差不多,不过是 cumtime 除以 ncalls
找到最值得优化的点,然后干吧。

第二招:一蛇禅:只需一招

记得刚开始接触 Python 的时候,有一位学长告诉我,Python 有一个牛逼的理想,它希望每一个用它的人能写出一模一样的程序。Python 之禅有云:

There should be one-- and preferably only one --obvious way to do it

所以 Python 系专业的禅师提供了一些常用功能的 only one 的写法。本人看了一下传说中的PythonWiKi:PerformanceTips,总结了几个「不要酱紫」「要酱紫」。

合并字符串的时候不要酱紫:

s = "" for substring in list: s += substring

要酱紫:

s = "".join(slist)

格式化字符串的时候不要酱紫:

out = "<html>" + head + prologue + query + tail + "</html>"

要酱紫:

out = "<html>%s%s%s%s</html>" % (head, prologue, query, tail)

可以不用循环的时候就不要用循环,比如不要酱紫:

newlist = [] for word in oldlist: newlist.append(word.upper())

要酱紫:

newlist = map(str.upper, oldlist)

或者酱紫:

newlist = [s.upper() for s in oldlist]

字典初始化,比较常用的:

wdict = {} for word in words: if word not in wdict: wdict[word] = 0 wdict[word] += 1

如果重复的 word 太多了的话,可以考虑用酱紫的模式来省掉大量判断:

wdict = {} for word in words: try: wdict[word] += 1 except KeyError: wdict[word] = 1

尽量减少 function 调用次数,用内部循环代替,比如,不要酱紫:

x = 0 def doit1(i): global x x = x + i list = range(100000) t = time.time() for i in list: doit1(i)

要酱紫:

x = 0 def doit2(list): global x for i in list: x = x + i list = range(100000) t = time.time() doit2(list)

第三招:蛇之狙击:高速搜索

这一招部分来源于IBM:Python 代码性能优化技巧,搜索算法的最高境界是O(1)的算法复杂度。也就是 Hash Table。本人幸本科的时候学了点数据结构。知道 Python 的 list 使用类似链表的方法实现的。如过列表很大的话,在茫茫多的项里面用 if X in list_a 来做搜索和判断效率是非常低的。

Python 的 tuple 我用得非常少,不评论。另两个我用得非常多的是 set 和 dict。这两个就是用的类似 Hash Table 的实现方法。

所以尽量不要酱紫:

k = [10,20,30,40,50,60,70,80,90] for i in xrange(10000): if i in k: #Do something continue

要酱紫:

``` k = [10,20,30,40,50,60,70,80,90] k_dict = {i:0 for i in k}

先把 list 转换成 dictionary

for i in xrange(10000): if i in k_dict: #Do something continue ```

找 list 的交集,不要酱紫:

list_a = [1,2,3,4,5]
list_b = [4,5,6,7,8]
list_common = [a for a in list_a if a in list_b]

要酱紫:

list_a = [1,2,3,4,5]
list_b = [4,5,6,7,8]
list_common = set(list_a)&set(list_b)

第四招:小蛇蛇……:想不出来名字了,就是各种小 Tips

变量交换不需要中间变量:a,b = b,a (这里有个神坑,至今记忆深刻:True,False = False,True)
如果使用 Python2.x,用 xrange 代替 range,如果用 Python3.x,range 已经是 xrange 了,xrange 已经木有了。xrange 不会像 range 一样生成一个列表,而是生成一个迭代器,省内存。
可以用 x>y>z 代替 x>y and y>z。效率更高,可读性也更好。当然理论上 x>y
add(x,y) 一般会比 a+b 要快?这个本人有所怀疑,实验了一下,首先 add 不能直接用,要 import operator,第二,我的实验结果表示 add(x,y) 完全没有 a+b 快,更何况还要牺牲可读性。
while 1 确实比 while True 要快那么一点点。做了两次实验,大概快了15%左右。
第五招:无蛇胜有蛇:代码之外的性能

代码之外嘛,除了硬件之外,就是编译器了,这里隆重推荐 pypy。pypy是一种叫做 just-in-time 的即时编译器。这个编译器的特点就是编译一句跑一句,和静态的编译器的区别嘛,我在知乎上看到一个非常形象的比喻:

假定你是一个导演,静态编译就是让演员把整个剧本背下来吃透,然后连续表演一个小时。动态编译就是让演员表演两分钟,然后思考一下,再看一下剧本,再表演两分钟……

动态编译和静态编译各有所长,看你演的是电影还是话剧了。

此外还有一个 Cython 可以在 python 里内置一些 C 的代码。我用的非常少,但是关键时刻确实有效。

Python 相关文章推荐
python类型强制转换long to int的代码
Feb 10 Python
Python使用minidom读写xml的方法
Jun 03 Python
在Django中进行用户注册和邮箱验证的方法
May 09 Python
Python简单实现安全开关文件的两种方式
Sep 19 Python
使用Python写CUDA程序的方法
Mar 27 Python
python技能之数据导出excel的实例代码
Aug 11 Python
python调用Delphi写的Dll代码示例
Dec 05 Python
Python反转序列的方法实例分析
Mar 21 Python
Windows下pycharm创建Django 项目(虚拟环境)过程解析
Sep 16 Python
浅谈keras中Dropout在预测过程中是否仍要起作用
Jul 09 Python
用python实现一个简单的验证码
Dec 09 Python
Python爬虫网络请求之代理服务器和动态Cookies
Apr 12 Python
python实现html转ubb代码(html2ubb)
Jul 03 #Python
Python 获取新浪微博的最新公共微博实例分享
Jul 03 #Python
python socket 超时设置 errno 10054
Jul 01 #Python
python设置检查点简单实现代码
Jul 01 #Python
如何搜索查找并解决Django相关的问题
Jun 30 #Python
python字典多条件排序方法实例
Jun 30 #Python
python实现的解析crontab配置文件代码
Jun 30 #Python
You might like
PHP 验证登陆类分享
2015/03/13 PHP
php ucwords() 函数将字符串中每个单词的首字符转换为大写(实现代码)
2016/05/12 PHP
php mysql like 实现多关键词搜索的方法
2016/10/29 PHP
jQuery 浮动广告实现代码
2008/12/25 Javascript
Js 回车换行处理的办法及replace方法应用
2013/01/24 Javascript
javascript陷阱 一不小心你就中招了(字符运算)
2013/11/10 Javascript
jquery div拖动效果示例代码
2013/12/08 Javascript
使用jquery实现的一个图片延迟加载插件(含图片延迟加载原理)
2014/06/05 Javascript
JQuery设置获取下拉菜单某个选项的值(比较全)
2014/08/05 Javascript
Nodejs异步回调的优雅处理方法
2014/09/25 NodeJs
简单的jQuery banner图片轮播实例代码
2016/03/04 Javascript
JavaScript数组去重的两种方法推荐
2016/04/05 Javascript
仿Angular Bootstrap TimePicker创建分钟数-秒数的输入控件
2016/07/01 Javascript
BootStrap无限级分类(无限极分类封装版)
2016/08/26 Javascript
js实现简单的网页换肤效果
2017/01/18 Javascript
详解nodejs实现本地上传图片并预览功能(express4.0+)
2017/06/28 NodeJs
VUE2.0 ElementUI2.0表格el-table自适应高度的实现方法
2018/11/28 Javascript
JavaScript寄生组合式继承原理与用法分析
2019/01/11 Javascript
mpvue实现左侧导航与右侧内容的联动
2019/10/21 Javascript
javascript设计模式 ? 解释器模式原理与用法实例分析
2020/04/17 Javascript
Python Requests安装与简单运用
2016/04/07 Python
python3 unicode列表转换为中文的实例
2018/10/26 Python
浅谈python下tiff图像的读取和保存方法
2018/12/04 Python
python3编写ThinkPHP命令执行Getshell的方法
2019/02/26 Python
使用PyQtGraph绘制精美的股票行情K线图的示例代码
2019/03/14 Python
Python3.5模块的定义、导入、优化操作图文详解
2019/04/27 Python
Python Numpy数组扩展repeat和tile使用实例解析
2019/12/09 Python
pymysql 插入数据 转义处理方式
2020/03/02 Python
详解python内置模块urllib
2020/09/09 Python
使用CSS3的box-sizing属性解决div宽高被内边距撑开的问题
2016/06/28 HTML / CSS
马来西亚网上购物平台:ezbuy
2018/02/13 全球购物
趣味比赛活动方案
2014/02/15 职场文书
艺术学院毕业生自我评价
2014/03/02 职场文书
2014年管理工作总结
2014/11/22 职场文书
Win10此设备不支持接收Miracast无法投影的解决方法
2022/07/07 数码科技
MySQL控制流函数(-if ,elseif,else,case...when)
2022/07/07 MySQL