`
fulerbakesi
  • 浏览: 570497 次
文章分类
社区版块
存档分类
最新评论

python 提高效率的几个小技巧

 
阅读更多

1.1. 最常见
一个最常见的速度陷坑(至少是俺在没看到网上这篇介绍时陷进去
过好些次的) 是: 许多短字串并成长字串时, 大家通常会用:

Toggle line numbers
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
模块:

Toggle line numbers
1 import profile
2 profile.run (’想要检查的函数名()’)
就会打印出那个函数里调用了几次其它函数, 各用了多少时间, 总共用了多少时间等信息 — Nice ? 详请参阅<<库参考>>中的 profile模块的论述.

当然脑袋笨一点或是聪明一点的, 也可以用time模块中的time() 来显示系统时间, 减去上次的time()就是与它的间隔秒数了.

1.1.2. 字串相并
就头上的例子而言, 用 :

Toggle line numbers
1 longStr =”.join(shortStrs)
立马搞定, 但如果shortStrs里面不都是字串, 而包含了些数 字呢 ? 直接用join就会出错. 不怕, 这样来:

Toggle line numbers
1 shortStrs = [str(s) for s in shortStrs[i]]
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', ...]中的每个字串变成大写, 可能会用:

Toggle line numbers
1 import string
2 newL = []
3 for s in l: newL.append( string.upper(s) )
用map就可以省去for循环的前头:

Toggle line numbers
1 import string
2 newL = map (string.upper, l)
Guido的文章讲得很详细.

1.1.5. 局域变量 及 ‘.’
象上面, 若用 append = newL.append, 及换种import方法:

Toggle line numbers
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', ...] 中每个词出现的次数, 你可能会:

Toggle line numbers
1 count = {}
2 for s in l:
3 if not count.has_key(s): count[s] = 0
4 else: count[s] += 1
由于每次都得在count中寻找是否已有同名关键词, 会很费时间. 而用try:

Toggle line numbers
1 count ={}
2 for s in l:
3 try: count[s] += 1
4 except KeyError: count[s] = 0
就好得多. 当然若经常出现例外时, 就不要用try了.

1.1.7. import语句
这好理解. 就是避免在函数定义中来import一个模块, 应全在 全局块中来import

1.1.8. 大量数据处理
由于Python 中的函数调用前头(overhead)比较重, 所以处理大量 数据时, 应:

Toggle line numbers
1 def f():
2 for d in hugeData: …
3 f()
而不要:

Toggle line numbers
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. 我们自个儿的体悟
请有心得者分享!

在“大量数据处理”小节里,是不是说,不要再循环体内部调用函数,应该把函数放到外面?从Python2.2开始,”找出速度瓶颈”,已经可以使用hotshot模块了.据说对程序运行效率的影响要比profile小. — jacobfan
“由于Python 中的函数调用前头(overhead)比较重, 所以处理大量 数据时, 应: ” 这句译文中,overhead翻译成”前头”好象不妥.翻译成”由于Python 中函数调用的开销比较大,…”要好些 — jacobfan
数组排序中讲的方法真的会快点吗? 真的快到我们值得放弃直接用sort得到得可读性吗?值得怀疑 — hoxide
Python2.4以后 sort和sorted的使用更加灵活,link已经加到文中,我没有比较过效率。-yichun
关于 “try的使用”:
其实setdefault方法就是为这个目的设的:

Toggle line numbers
1 count = {}
2 for s in l:
3 count.setdefault(s, 0) += 1
这个其实能做更多。通常遇到的问题是要把类似的东西group起来,所以你可能想用:

Toggle line numbers
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’)))

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/guzicheng/archive/2010/10/13/5939222.aspx

说明:增加代码的描述力,可以成倍减少你的LOC,做到简单,并且真切有力
观点:少打字=多思考+少出错,10代码行比50行更能让人明白,以下技巧有助于提高5倍工作效率

1. 交换变量值时避免使用临时变量:(cookbook1.1)
老代码:我们经常很熟练于下面的代码
temp = x
x = y
y = temp
代码一:
u, v, w = w, v, u
有人提出可以利用赋值顺序来简化上面的三行代码成一行
代码二:
u, v = v, u
其实利用Python元组赋值的概念,可更简明 -- 元组初始化 + 元组赋值

2. 读字典时避免判断键值是否存在:(cookbook1.2)
d = { 'key': 'value' }
老代码:
if 'key' in d: print d['key']
else: print 'not find'
新代码:
print d.get('key', 'not find')

3. 寻找最小值和位置的代码优化
s = [ 4,1,8,3 ]
老代码:
mval, mpos = MAX, 0
for i in xrange(len(s)):
if s[i ]
观点一:用Python编程,需要有“一字千金”的感觉;既然选择了Python,就不要在意单条语句的效率。
上面几点例子很基础,实际中将原始代码压缩1/5并不是不可能,我们之前一个子项目,C++代码270K,重构后Python代码只有67K,当然使用python的日志模块(logging),读写表格文本(csv)等,也功不可末,最终代码变成原来的1/4,我觉得自己的寿命延长了三倍。。。下面优化几个常用代码:

4. 文件读取工作的最简单表达:
老代码:我们需要将文本文件读入到内存中
line = ''
fp = open('text.txt', 'r')
for line in fp: text += line
代码一:
text = string.join([ line for line in open('text.txt')], '']
代码二:
text = ''.join([ line for line in open('text.txt')])
代码三:
text = file('text.txt').read()
新版本的Python可以让你写出比1,2漂亮的代码(open是file的别名,这里file更直观)

5. 如何在Python实现三元式:
老代码:用惯C++,Java,C#不喜欢写下面代码
if n >= 0: print 'positive'
else: print 'negitive'
代码一:该技巧在 Lua里也很常见
print (n >= 0) and 'positive' or 'negitive'
说明:这里的'and'和'or'相当于C中的':'和'?'的作用,道理很简单,因为如果表达式为真了那么后面的or被短路,取到'positive';否则,and被短路,取到'negitive'
代码二:
print (n >= 0 and ['positive'] or ['negitive])[0]
说明:将两个值组装成元组,即使'positive'是None, '', 0 之类整句话都很安全
代码三:
print ('negitive', 'positive')[n >= 0]
说明:(FalseValue, TrueValue)[Condition] 是利用了 元组访问 + True=1 两条原理

6. 避免字典成员是复杂对象的初始化:(cookbook1.5)
老代码:
if not y in d: d[y] = { }
d[y][x] = 3
新代码:
d.setdefault(y, { })[x] = 3
如果成员是列表的话也一样: d.setdefault(key, []).append(val)
上面六点技巧加以发挥,代码已经很紧凑了,但是还没有做到“没有一句废话”可能有人怀疑真的能减少1/5的代码么??我要说的是1/5其实很保守,Thinking in C++的作者后来用了Python以后觉得Python甚至提高了10倍的工作效率。下面的例子可以进一步说明:
例子1:把文本的IP地址转化为整数
说明:需要将类似'192.168.10.214'的IP地址转化为 0x0C0A80AD6,在不用 inet_aton情况下。当C++/Java程序员正为如何进行文本分析,处理各种错误输入烦恼时,Python程序员已经下班:
f = lambda ip: sum( [ int(k)*v for k, v in zip(ip.split('.'), [1
首先ip.split('.')得到列表['192','168','10','214'],经过zip一组装,就变成
[('192',0x1000000),('168',0x10000),('10',0x100),('214',1)]
接着for循环将各个元组的两项做整数乘法,最后将新列表的值用sum求和,得到结果
C++程序员不肖道:“你似乎太相信数据了,根本没有考虑道错误的输入”
Python程序员回答:“外面的try/except已帮我完成所有异常处理,不必担心越界崩溃而无法捕获”
Java程序员得意的看着自己百行代码:“我想知道你如何让你的同事来理解你的杰作?你有没有考虑过将类似gettoken之类的功能独立处理,让类似问题可以复用?我的代码说明了如何充分发挥Reflection和interface的优秀特性,在增加重用性的同时,提供清晰可读的代码”
Python无奈道:“这是‘纯粹的代码’,意思是不可修改,类似正则表达式,只要让人明白他的功能就行了,要修改就重写。再我能用三行代码完成以内绝不会有封装的想法,况且熟悉Python者也不觉得难读啊?”
C++程序员抛出杀手简:“如果让你一秒钟处理10w个ip转化的话怎么办?”
Python程序员觉得想睡觉:“你觉得我会蠢到还用Python做这样的事情么?”
此时C++程序员似乎并没听到,反而开始认真的思考起自己刚才提出问题来,一会只见他轻藐的看了另外两人一眼,然后胸有成竹的转到电脑前,开始往屏幕上输入: “template <....”
小笑话:封装的陷阱,让人一边喊着“封装”或“复用”,一边在新项目中,全部打破重写,并解释为--重构
观点二:简单即是美,把一个东西设计复杂了,本身就是有问题的
思考题:上面的程序,如果反过来,将ip的整数形式转化为字符串,各位该如何设计呢??
例子2:输出一个对象各个成员的名称和值
g = lambda m: '\n'.join([ '%s=%s'%(k, repr(v)) for k, v in m.__dict__.iteritems() ])
用法:print g(x)
延伸:上面两个例子熟悉了lambda以后,建议可以尝试使用下 yield

观点总结
Q:“怎样才算做到注重What you think多于What you are writing”
A:“就是说你手上打着第1页需求的代码,眼睛却在看着第2页需求的内容,心里想着如何应对5-10页的东西”
国外多年前废除PASCAL改用Python做科研教学是有道理的,关于精简代码的例子举不胜举,用它编码时应该有“一字千金”的感觉,否则最终写出来的,还是“伪装成Python的C++程序”。
编程本来就是快乐的,避免过多的体力劳动,赢得更多思考的时间。
思考题:到底是封装呢?还是放弃封装?
思考题:“more than one way to do it”是不是就是好事?它的反面是什么?


点击打开链接

分享到:
评论

相关推荐

    python入门教程:18 个 Python 高效编程小技巧.docx

    ### Python高效编程小技巧详解 #### 一、交换变量 在传统的C++等语言中,如果需要交换两个变量的值,通常需要引入第三个临时变量来进行操作。而在Python中,你可以非常简便地完成这一过程,无需额外变量。例如,...

    Python实用小工具大全

    本篇文章将深入探讨标题“Python实用小工具大全”所涵盖的几个关键知识点,包括图片爬取、雷达图绘制、鸡兔同笼问题求解以及螺旋线绘制,同时也涉及到了Python的集成开发环境(IDE)的安装包。 首先,我们来讨论...

    Python中的一些陷阱与技巧小结

    本文将从以下几个方面进行详细介绍: #### 一、Python中的常见陷阱 1. **生成器的误用** 生成器是一种特殊的迭代器,它们允许程序员编写一个可以记住执行状态的函数。使用`yield`关键字代替`return`来创建生成器...

    Python如何处理大数据?3个技巧效率提升攻略(推荐)

    不用在线等,给几个错误示范:有人用multiprocessing 处理,但是效率非常低。于是,有人用python处理大文件还是会存在效率上的问题。因为效率只是和预期的时间有关,不会报错,报错代表程序本身出现问题了~ 所以,...

    python小游戏

    通过分析这个项目,我们可以深入学习以下几个关键知识点: 1. **Python基础知识**:Python是一种高级编程语言,以其简洁明了的语法著称。在编写五子棋游戏时,我们需要掌握变量、数据类型(如字符串、列表、元组)...

    Python你需要知道的20个常用的Python技巧

    根据提供的标题、描述、标签以及部分内容,我们可以详细探讨这20个Python编程技巧中的几个示例,以此为基础进一步深入理解这些技巧如何提升编程效率及代码的可读性。 ### 1. 字符串反转 使用Python切片进行字符串...

    80个Python经典资料(教程+源码+工具)汇总——下载目录

    这些资源按照类型大致可以分为以下几个部分: 1. **Python专题资料**:提供了一系列围绕特定主题或领域的深入学习材料。 2. **学习资料**:涵盖了从Python基础知识到高级应用的广泛内容。 3. **源码资料**:提供了...

    Python微信打飞机小游戏

    在这款游戏中,主要涉及到以下几个关键知识点: 1. **Pygame基本概念**:首先,你需要了解Pygame的基本架构,包括窗口初始化、事件处理、图像绘制和音频播放。`main.py`应该是游戏的主程序入口,它会初始化Pygame...

    分享python数据统计的一些小技巧

    以上介绍的几个小技巧不仅简单实用,而且能够大大提高 Python 数据统计分析工作的效率。通过熟练掌握这些技巧,可以在面对复杂数据时更加从容不迫,同时也能够更高效地完成工作任务。无论是初学者还是经验丰富的...

    基于python王者荣耀脚本

    在“AutoGame”脚本中,我们可能涉及以下几个核心知识点: 1. **图像识别与处理**:为了使脚本能够识别游戏中的元素,如英雄、敌方、小兵等,我们需要用到OpenCV或者PIL这样的图像处理库。通过对屏幕截图的像素分析...

    Python爬虫实战和Sublime技巧

    在Python爬虫实战中,你需要了解以下几个关键知识点: 1. 网络请求:理解HTTP协议,如何使用requests库发送GET和POST请求。 2. HTML与CSS选择器:学会使用BeautifulSoup解析HTML,通过CSS选择器定位元素。 3. 正则...

    python实现五子棋游戏程序

    在这个Python实现中,我们可能会涉及到以下几个关键知识点: 1. **图形用户界面(GUI)**:描述中提到“界面都有”,这意味着项目可能使用了Python的GUI库,如Tkinter、PyQt或wxPython等,来创建游戏的交互界面。...

    Python零基础入门学习教程

    根据给定的文件信息,我们将围绕“Python零基础入门学习教程”这一主题展开详细的知识点讲解,主要包括Python的安装、基础知识、进阶技巧以及实战应用等几个方面。 ### Python安装 在开始Python的学习之旅之前,...

    Python经典题库及答案-python-试题.docx

    在准备Python考试或学习时,了解并掌握以下几个关键知识点至关重要: 1. **导入模块**: - `import` 关键字用于导入整个模块,如 `import math` 导入数学模块。 - `from ... import *` 可以导入模块中的所有对象...

    kNN分类器和两个实例-Python

    - **剪枝策略**:为了提高效率,可以采用kd树、球树等数据结构进行剪枝,减少不必要的距离计算。 总的来说,kNN是一种简单但有效的分类方法,适用于多种实际问题。Python的实现使它更易于理解和使用,但在处理大...

    cpp-一个Python列表替换针对同类集合进行了优化

    在从Python到C++的数据转换中,优化可能包括以下几个方面: 1. **内存管理**:Python列表中的对象通常是引用计数的,而C++需要手动管理内存。优化可能包括减少内存分配次数,使用智能指针(如std::shared_ptr或std:...

    CEC2017_Python.zip

    5. **代码实现技巧**:观察Python在实现计算智能算法时的效率和可读性。 通过深入学习和分析这个压缩包中的内容,不仅可以掌握PSO算法的实践应用,还能了解到计算智能领域最新的研究进展和挑战。对于想要提升自己在...

    Programming in Python 3 - A Complete Introduction to the Python Language 2nd

    - **项目开发**:通过几个具体的项目案例,展示如何将前面所学的知识点应用于实际问题的解决中。 - **网络编程**:介绍如何使用Python进行简单的网络通信编程,如TCP/IP协议的应用。 - **数据库操作**:教授如何使用...

    24点游戏(Python)

    在分析“p24.py”时,我们可以预期以下几个关键知识点: 1. **基本数据类型和操作**:包括整数、浮点数的操作,以及可能涉及的数学函数。 2. **列表(List)**:用于存储和管理数字组合,可能用到列表的遍历、添加、...

Global site tag (gtag.js) - Google Analytics