代碼優(yōu)化Part1<a id="sec-1" name="sec-1"></a>
分享最近看到的關(guān)于代碼優(yōu)化的一些技巧闪盔。
if 判斷的短路特性<a id="sec-1-1" name="sec-1-1"></a>
對(duì)于and蹲堂,應(yīng)該把滿足條件少的放在前面,這樣當(dāng)對(duì)于大量判斷時(shí)楔绞, 滿足條件少的情況直接回導(dǎo)致其后其他表達(dá)式不會(huì)計(jì)算從而節(jié)約時(shí)間(因?yàn)?False and True 還是 False)
import timeit
s1 = """
a = range(2000)
[i for i in a if i % 2 ==0 and i > 1900]
"""
s2 = """
a = range(2000)
[i for i in a if i > 1900 and i % 2 ==0]
"""
print timeit.timeit(stmt=s1, number=1000)
print timeit.timeit(stmt=s2, number=1000)
運(yùn)行結(jié)果如下:
? python test6.py
0.248532056808
0.195827960968
# 可以看到s2 表達(dá)式計(jì)算更快读恃, 因?yàn)榇蟛糠智闆r都不滿足 i>1900, 所以這些情況下, i % 2 == 0 也沒(méi)有計(jì)算,從而節(jié)約了時(shí)間
同理對(duì)于or芬膝,把滿足條件多的放在前面悲没。
import timeit
s1 = """
a = range(2000)
[i for i in a if 10 < i <20 or 1000 < i < 2000]
"""
s2 = """
a = range(2000)
[i for i in a if 1000 < i < 2000 or 10 < i <20]
"""
print timeit.timeit(stmt=s1, number=1000)
print timeit.timeit(stmt=s2, number=1000)
運(yùn)行結(jié)果:
0.253124952316
0.202992200851
join 合并字符串<a id="sec-1-2" name="sec-1-2"></a>
join 合并字符串比循環(huán)使用 +
來(lái)合并要快剪决。
import timeit
s1 = """
a = [str(x) for x in range(2000)]
s = ''
for i in a:
s += i
"""
s2 = """
a = [str(x) for x in range(2000)]
s = ''.join(a)
"""
print timeit.timeit(stmt=s1, number=1000)
print timeit.timeit(stmt=s2, number=1000)
運(yùn)行結(jié)果如下:
python test6.py
0.558945894241
0.422435998917
while 1 和 while True<a id="sec-1-3" name="sec-1-3"></a>
在python2.x里, True 和 False 不是保留的關(guān)鍵字檀训,是一個(gè)全局變量,這意味著你可以這樣
>>> True = 0
>>> True
0
>>> if not True:
... print '1'
...
1
所以下面這兩種情況:
import timeit
s1 = """
n = 1000000
while 1:
n -= 1
if n <= 0: break
"""
s2 = """
n = 1000000
while True:
n -= 1
if n <= 0: break
"""
print timeit.timeit(stmt=s1, number=100)
print timeit.timeit(stmt=s2, number=100)
運(yùn)行結(jié)果如下:
? python test6.py
5.18007302284
6.84624099731
因?yàn)槊看闻袛?while True
的時(shí)候享言, 先要去找到True的值峻凫。
在python3.x里, True
變成了關(guān)鍵字參數(shù)览露,所以上述兩種情況就一樣了荧琼。
cProfile, cStringIO 和 cPickle<a id="sec-1-4" name="sec-1-4"></a>
使用C語(yǔ)言的版本寫(xiě)的擴(kuò)展要比原生的要快。cPickle vs pickle 如下:
import timeit
s1 = """
import cPickle
import pickle
n = range(10000)
cPickle.dumps(n)
"""
s2 = """
import cPickle
import pickle
n = range(10000)
pickle.dumps(n)
"""
print timeit.timeit(stmt=s1, number=100)
print timeit.timeit(stmt=s2, number=100)
運(yùn)行結(jié)果如下:
? python test6.py
0.182178974152
1.70917797089
合理使用生成器<a id="sec-1-5" name="sec-1-5"></a>
區(qū)別<a id="sec-1-5-1" name="sec-1-5-1"></a>
使用()得到的是一個(gè)generator對(duì)象差牛,所需要的內(nèi)存空間與列表的大小無(wú)關(guān)命锄,所以效率會(huì)高一些。
import timeit
s1 = """
[i for i in range (100000)]
"""
s2 = """
(i for i in range(100000))
"""
print timeit.timeit(stmt=s1, number=1000)
print timeit.timeit(stmt=s2, number=1000)
結(jié)果:
? python test6.py
5.44327497482
0.923446893692
但是對(duì)于需要循環(huán)遍歷的情況:使用迭代器效率反而不高偏化,如下:
import timeit
s1 = """
ls = range(1000000)
def yield_func(ls):
for i in ls:
yield i+1
for x in yield_func(ls):
pass
"""
s2 = """
ls = range(1000000)
def not_yield_func(ls):
return [i+1 for i in ls]
for x in not_yield_func(ls):
pass
"""
print timeit.timeit(stmt=s1, number=10)
print timeit.timeit(stmt=s2, number=10)
結(jié)果如下:
? python test6.py
1.03186702728
1.01472687721
所以使用生成器是一個(gè)權(quán)衡的結(jié)果脐恩,對(duì)于內(nèi)存、速度綜合考慮的結(jié)果侦讨。
xrange<a id="sec-1-5-2" name="sec-1-5-2"></a>
在python2.x里xrange 是純C實(shí)現(xiàn)的生成器驶冒,相對(duì)于range來(lái)說(shuō)苟翻,它不會(huì)一次性計(jì)算出所有值在內(nèi)存中。但它的限制是只能和整型一起工作:你不能使用long或者float骗污。
import 語(yǔ)句的開(kāi)銷<a id="sec-1-6" name="sec-1-6"></a>
import語(yǔ)句有時(shí)候?yàn)榱讼拗扑鼈兊淖饔梅秶蛘吖?jié)省初始化時(shí)間崇猫,被卸載函數(shù)內(nèi)部,雖然python的解釋器不會(huì)重復(fù)import同一個(gè)模塊不會(huì)出錯(cuò)需忿,但重復(fù)導(dǎo)入會(huì)影響部分性能诅炉。
有時(shí)候?yàn)榱藢?shí)現(xiàn)懶加載(即使用的時(shí)候再加載一個(gè)開(kāi)銷很大的模塊),可以這么做:
email = None
def parse_email():
global email
if email is None:
import email
...
# 這樣一來(lái)email模塊僅會(huì)被引入一次屋厘,在parse_email()被第一次調(diào)用的時(shí)候涕烧。
參考資源:<a id="sec-2" name="sec-2"></a>
- https://wiki.python.org/moin/PythonSpeed/PerformanceTips
- http://blog.csdn.net/zhoudaxia/article/details/23853609
- https://www.ibm.com/developerworks/cn/linux/l-cn-python-optim/