设为首页 收藏本站
查看: 1304|回复: 0

[经验分享] 提升Python程序速度技巧汇总

[复制链接]

尚未签到

发表于 2015-4-25 05:14:05 | 显示全部楼层 |阅读模式
  1. Python编程速度技巧
  1.1. 最常见
  * 一个最常见的速度陷坑(至少是俺在没看到网上这篇介绍时陷进去
  过好些次的) 是: 许多短字串并成长字串时, 大家通常会用:
  切换行号显示
  1 shortStrs = [ str0, str1, ..., strN]
2
N+1个字串所组成的数列
3 longStr = ''
4 for s in shortStrs: longStr += s
  因为Python里字串是不可变的, 所以每次 longStr += s 都是将原 来的 longStr 与 str 拷贝成一个新字串, 再赋给longStr. 随着 longStr的不断增长, 所要拷贝的内容越来越长. 最后导至str0被 拷贝N+1次, str1是N次, ... .
  那咋办呢 ? 咱们来看看Skip Montanaro先生的解说: http://musi-cal.mojam.com/~skip/python/fastpython.html 及可参考一下Guido van Rossum本人的:http://www.python.org/doc/essays/list2str.html
  1.1.1. 找出速度瓶颈
  * 1)首先在大家应先学会怎么去找出速度瓶颈: Python自带有profile
  模块:
  切换行号显示
  1 import profile
2 profile.run ('想要检查的函数名()')
  就会打印出那个函数里调用了几次其它函数, 各用了多少时间, 总共用了多少时间等信息 --- Nice ? 详请参阅中的 profile模块的论述.
  当然脑袋笨一点或是聪明一点的, 也可以用time模块中的time() 来显示系统时间, 减去上次的time()就是与它的间隔秒数了.
  1.1.2. 字串相并
  * 就头上的例子而言, 用 :
  切换行号显示
  1 longStr =''.join(shortStrs)
  立马搞定, :) 但如果shortStrs里面不都是字串, 而包含了些数 字呢 ? 直接用join就会出错. 不怕, 这样来:
  切换行号显示
  1 shortStrs = [str(s) for s in shortStrs]
2 longStr = ''.join(shortStrs)
  也即先将数列中所有内容都转化为字串, 再用join.
  对少数几个字串相并, 应避免用: all = str0 + str1 + str2 + str3 而用: all = '%s%s%s%s' % (str0, str1, str2, str3)
  1.1.3. 数列排序
  * list.sort ()
  你可以按特定的函数来: list.sort( 函数 ), 只要这个函数接受 两参数, 并按特定规则返回1, 0, -1就可以. --- 很方便吧? 但 会大大减慢运行速度. 下面的方法, 俺举例子来说明可能更容易 明白.
  比方说你的数列是 l = ['az', 'by'], 你想以第二个字母来排序. 先取出你的关键词, 并与每个字串组成一个元组: new = map (lambda s: (s[1], s), l )
  于是new变成[('z', 'az'), ('y', 'by')], 再把new排一下序: new.sort()
  则new就变成 [('y', 'by'), ('z', 'az')], 再返回每个元组中 的第二个字串: sorted = map (lambda t: t[1], new)
  于是sorted 就是: ['by', 'az']了. 这里的lambda与map用得很 好.
  *
  Python2.4以后, sort和sorted的使用可以参考这片 Wiki: HowToSort
  
  1.1.4. 循环
  比如for循环. 当循环体很简单时, 则循环的调用前头(overhead) 会显得很臃肿, 此时map又可以帮忙了. 比如你想把一个长数列 l=['a', 'b', ...]中的每个字串变成大写, 可能会用:
  切换行号显示
  1 import string
2 newL = []
3 for s in l: newL.append( string.upper(s) )
  用map就可以省去for循环的前头:
  切换行号显示
  1 import string
2 newL = map (string.upper, l)
  Guido的文章讲得很详细.
  1.1.5. 局域变量 及 '.'
  象上面, 若用 append = newL.append, 及换种import方法:
  切换行号显示
  1 import string
2 append = newL.append
3 for s in l: append (string.upper(s))
  会比在for中运行newL.append快一些, 为啥? 局域变量容易寻找.
  俺自己就不比较时间了, Skip Montanaro的结果是:
  基本循环: 3.47秒
去点用局域变量: 1.79秒
使用map: 0.54秒
  1.1.6. try的使用
  比如你想计算一个字串数列: l = ['I', 'You', 'Python', 'Perl', ...] 中每个词出现的次数, 你可能会:
  切换行号显示
  1 count = {}
2 for s in l:
3 if not count.has_key(s): count = 0
4 else: count += 1
  由于每次都得在count中寻找是否已有同名关键词, 会很费时间. 而用try:
  切换行号显示
  1 count ={}
2 for s in l:
3 try: count += 1
4 except KeyError: count = 0
  就好得多. 当然若经常出现例外时, 就不要用try了.
  1.1.7. import语句
  这好理解. 就是避免在函数定义中来import一个模块, 应全在 全局块中来import
  1.1.8. 大量数据处理
  由于Python中的函数调用前头(overhead)比较重, 所以处理大量 数据时, 应:
  切换行号显示
  1 def f():
2 for d in hugeData: ...
3 f()
  而不要:
  切换行号显示
  1 def f(d): ...
2 for d in hugeData: f(d)
  这点好象对其它语言也适用, 差不多是放之四海而皆准, 不过对 解释性语言就更重要了.
  1.1.9. 减少周期性检查
  这是Python的本征功能: 周期性检查有没有其它绪(thread)或系 统信号(signal)等要处理.
  可以用sys模块中的setcheckinterval 来设置每次检查的时间间隔.
  缺省是10, 即每10个虚拟指令 (virtual instruction)检查一次.
  当你不用绪并且也懒得搭理 系统信号时, 将检查周期设长会增加速度, 有时还会很显著.
  ---编/译完毕. 看来Python是易学难精了, 象围棋?
  2. 我们自个儿的体悟
  请有心得者分享!
  2.1. 故事
  *
  Python性能调优 ~ flydudu 分享
  2.2. 思考
  * 在“大量数据处理”小节里,是不是说,不要再循环体内部调用函数,应该把函数放到外面?从Python2.2开始,"找出速度瓶颈",已经可以使用hotshot模块了.据说对程序运行效率的影响要比profile小. -- jacobfan
* "由于Python中的函数调用前头(overhead)比较重, 所以处理大量 数据时, 应: " 这句译文中,overhead翻译成"前头"好象不妥.翻译成"由于Python中函数调用的开销比较大,..."要好些 -- jacobfan
* 数组排序中讲的方法真的会快点吗? 真的快到我们值得放弃直接用sort得到得可读性吗?值得怀疑 -- hoxide
* Python2.4以后 sort和sorted的使用更加灵活,link已经加到文中,我没有比较过效率。-yichun
* 关于 “try的使用”:
  其实setdefault方法就是为这个目的设的:
  切换行号显示
  1 count = {}
2 for s in l:
3 count.setdefault(s, 0) += 1
  这个其实能做更多。通常遇到的问题是要把类似的东西group起来,所以你可能想用:
  切换行号显示
  1 count = {}
2 for s in l:
3 count.setdefault(s, []).append(s)
  但是这样你只能把同样的东西hash起来,而不是一类东西。比如说你有一个dict构成的list叫sequence,需要按这些dict的某个key value分类,你还要对分类后的每个类别里面的这些dict各作一定的操作,你就需要用到Raymond实现的这个groupby,你就可以写:
  totals = dict((key, group)
for key, group in groupby(sequence, lambda x: x.get('Age')))
  - yichun
  * shortStrs = [str(s) for s in shortStrs]这句我在python2.5下报错(i未定义),我改成shortStrs = [str(s) for s in shortStrs]就可以了。
  
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-60284-1-1.html 上篇帖子: python Property属性用法 下篇帖子: 黄聪:使用Python中的HTMLParser、cookielib抓取和解析网页、从HTML文档中提取链接、图像、文本、Cookies(二)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表