# 使用python性能优化技巧的总结

python 代码优化常见技巧

o(1) -> o(lg n) -> o(n lg n) -> o(n^2) -> o(n^3) -> o(n^k) -> o(k^n) -> o(n!)

python 字典中使用了 hash table，因此查找操作的复杂度为 o(1)，而 list 实际是个数组，在 list 中，查找需要遍历整个 list，其复杂度为 o(n)，因此对成员的查找访问等操作字典要比 list 更快。

t = time()
list = [‘a’,’b’,’is’,’python’,’jason’,’hello’,’hill’,’with’,’phone’,’test’,
‘dfdf’,’apple’,’pddf’,’ind’,’basic’,’none’,’baecr’,’var’,’bana’,’dd’,’wrd’]
#list = dict.fromkeys(list,true)
print list
filter = []
for i in range (1000000):
for find in [‘is’,’hat’,’new’,’list’,’old’,’.’]:
if find not in list:
filter.append(find)
print “total run time:”
print time()-t

set 的 union， intersection，difference 操作要比 list 的迭代要快。因此如果涉及到求 list 交集，并集或者差的问题可以转换为 set 来操作。

t = time()
lista=[1,2,3,4,5,6,7,8,9,13,34,53,42,44]
listb=[2,4,6,9,23]
intersection=[]
for i in range (1000000):
for a in lista:
for b in listb:
if a == b:
intersection.append(a)
print “total run time:”
print time()-t

total run time:
38.4070000648清单 3. 使用 set 求交集 from time import time
t = time()
lista=[1,2,3,4,5,6,7,8,9,13,34,53,42,44]
listb=[2,4,6,9,23]
intersection=[]
for i in range (1000000):
list(set(lista)&set(listb))
print “total run time:”
print time()-t

t = time()
lista = [1,2,3,4,5,6,7,8,9,10]
listb =[0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,0.01]
for i in range (1000000):
for a in range(len(lista)):
for b in range(len(listb)):
x=lista[a]+listb[b]
print “total run time:”
print time()-t

t = time()
lista = [1,2,3,4,5,6,7,8,9,10]
listb =[0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,0.01]
len1=len(lista)
len2=len(listb)
for i in xrange (1000000):
for a in xrange(len1):
temp=lista[a]
for b in xrange(len2):
x=temp+listb[b]
print “total run time:”
print time()-t

python 中条件表达式是 lazy evaluation 的，也就是说如果存在条件表达式 if x and y，在 x 为 false 的情况下 y 表达式的值将不再计算。因此可以利用该特性在一定程度上提高程序效率。

t = time()
abbreviations = [‘cf.’, ‘e.g.’, ‘ex.’, ‘etc.’, ‘fig.’, ‘i.e.’, ‘mr.’, ‘vs.’]
for i in range (1000000):
for w in (‘mr.’, ‘hat’, ‘is’, ‘chasing’, ‘the’, ‘black’, ‘cat’, ‘.’):
if w in abbreviations:
#if w[-1] == ‘.’ and w in abbreviations:
pass
print “total run time:”
print time()-t

python 中的字符串对象是不可改变的，因此对任何字符串的操作如拼接，修改等都将产生一个新的字符串对象，而不是基于原字符串，因此这种持续的 copy 会在一定程度上影响 python 的性能。对字符串的优化也是改善性能的一个重要的方面，特别是在处理文本较多的情况下。字符串的优化主要集中在以下几个方面：

t = time()
s = “”
list = [‘a’,’b’,’b’,’d’,’e’,’f’,’g’,’h’,’i’,’j’,’k’,’l’,’m’,’n’]
for i in range (10000):
for substr in list:
s+= substr
print “total run time:”
print time()-t

s = “”
for x in list:
s += func(x)

slist = [func(elt) for elt in somelist]
s = “”.join(slist)

out = “%s%s%s%s” % (head, prologue, query, tail)

out = “” + head + prologue + query + tail + “”使用列表解析（list comprehension）和生成器表达式（generator expression）

from time import time
t = time()
list = [‘a’,’b’,’is’,’python’,’jason’,’hello’,’hill’,’with’,’phone’,’test’,
‘dfdf’,’apple’,’pddf’,’ind’,’basic’,’none’,’baecr’,’var’,’bana’,’dd’,’wrd’]
total=[]
for i in range (1000000):
for w in list:
total.append(w)
print “total run time:”
print time()-t

for i in range (1000000):
a = [w for w in list]

>>> from timeit import timer
>>> timer(“t=a;a=b;b=t”,”a=1;b=2″).timeit()
0.25154118749729365
>>> timer(“a,b=b,a”,”a=1;b=2″).timeit()
0.17156677734181258
>>>

if done is not none 比语句 if done != none 更快，读者可以自行验证；

while 1 要比 while true 更快（当然后者的可读性更好）；

profile 的使用非常简单，只需要在使用之前进行 import 即可。具体实例如下：

def profiletest():
total =1;
for i in range(10):
total=total*(i+1)
print total
if __name__ == “__main__”:
profile.run(“profiletest()”)

ncalls：表示函数调用的次数；

tottime：表示指定函数的总的运行时间，除掉函数中调用子函数的运行时间；

percall：（第一个 percall）等于 tottime/ncalls；

cumtime：表示该函数及其所有子函数的调用运行的时间，即函数开始调用到返回的时间；

percall：（第二个 percall）即函数运行一次的平均时间，等于 cumtime/ncalls；

filename:lineno(function)：每个函数调用的具体信息；

import pstats
p = pstats.stats(‘testprof’)
p.sort_stats(“name”).print_stats()

python 性能优化工具

python 性能优化除了改进算法，选用合适的数据结构之外，还有几种关键的技术，比如将关键 python 代码部分重写成 c 扩展模块，或者选用在性能上更为优化的解释器等，这些在本文中统称为优化工具。python 有很多自带的优化工具，如 psyco，pypy，cython，pyrex 等，这些优化工具各有千秋，本节选择几种进行介绍。

psyco

psyco 是一个 just-in-time 的编译器，它能够在不改变源代码的情况下提高一定的性能，psyco 将操作编译成有点优化的机器码，其操作分成三个不同的级别，有”运行时”、”编译时”和”虚拟时”变量。并根据需要提高和降低变量的级别。运行时变量只是常规 python 解释器处理的原始字节码和对象结构。一旦 psyco 将操作编译成机器码，那么编译时变量就会在机器寄存器和可直接访问的内存位置中表示。同时 python 能高速缓存已编译的机器码以备今后重用，这样能节省一点时间。但 psyco 也有其缺点，其本身运行所占内存较大。目前 psyco 已经不在 python2.7 中支持，而且不再提供维护和更新了，对其感兴趣的可以参考 http://psyco.sourceforge.net/

pypy

pypy 表示 “用 python 实现的 python”，但实际上它是使用一个称为 rpython 的 python 子集实现的，能够将 python 代码转成 c， .net， java 等语言和平台的代码。pypy 集成了一种即时 (jit) 编译器。和许多编译器，解释器不同，它不关心 python 代码的词法分析和语法树。 因为它是用 python 语言写的，所以它直接利用 python 语言的 code object.。 code object 是 python 字节码的表示，也就是说， pypy 直接分析 python 代码所对应的字节码 ,，这些字节码即不是以字符形式也不是以某种二进制格式保存在文件中， 而在 python 运行环境中。目前版本是 1.8. 支持不同的平台安装，windows 上安装 pypy 需要先下载 https://bitbucket.org/pypy/pypy/downloads/pypy-1.8-win32.zip，然后解压到相关的目录，并将解压后的路径添加到环境变量 path 中即可。在命令行运行 pypy，如果出现如下错误：”没有找到 msvcr100.dll, 因此这个应用程序未能启动，重新安装应用程序可能会修复此问题”，则还需要在微软的官网上下载 vs 2010 runtime libraries 解决该问题。具体地址为http://www.microsoft.com/download/en/details.aspx?displaylang=en&help”, “copyright”, “credits” or “license” for more information.
and now for something completely different: “pypy is vast, and contains
multitudes”
>>>>

c:\documents and settings\administrator\ 桌面 \doc\python>pypy loop.py
total run time:
8.42199993134
c:\documents and settings\administrator\ 桌面 \doc\python>python loop.py
total run time:
106.391000032

cython

cython 是用 python 实现的一种语言，可以用来写 python 扩展，用它写出来的库都可以通过 import 来载入，性能上比 python 的快。cython 里可以载入 python 扩展 ( 比如 import math)，也可以载入 c 的库的头文件 ( 比如 :cdef extern from “math.h”)，另外也可以用它来写 python 代码。将关键部分重写成 c 扩展模块

linux cpython 的安装：

[root@v5254085f259 cpython]# wget -n http://cython.org/release/cython-0.15.1.zip
–2012-04-16 22:08:35– http://cython.org/release/cython-0.15.1.zip
resolving cython.org… 128.208.160.197
connecting to cython.org|128.208.160.197|:80… connected.
http request sent, awaiting response… 200 ok
length: 2200299 (2.1m) [application/zip]
saving to: `cython-0.15.1.zip’
100%[======================================>] 2,200,299 1.96m/s in 1.1s
2012-04-16 22:08:37 (1.96 mb/s) – `cython-0.15.1.zip’ saved [2200299/2200299]

[root@v5254085f259 cpython]# unzip -o cython-0.15.1.zip

python setup.py install

[root@v5254085f259 cython-0.15.1]# cython
cython (http://cython.org) is a compiler for code written in the
cython language. cython is based on pyrex by greg ewing.
usage: cython [options] sourcefile.{pyx,py} …
options:
-v, –version display version number of cython compiler
-l, –create-listing write error messages to a listing file
-i, –include-dir search for include files in named directory
(multiple include directories are allowed).
-o, –output-file specify name of generated c file
-t, –timestamps only compile newer source files
-f, –force compile all source files (overrides implied -t)
-q, –quiet don’t print module names in recursive mode
-v, –verbose be verbose, print file names on multiple compil ation
-p, –embed-positions if specified, the positions in cython files of each
function definition is embedded in its docstring.
–cleanup
release interned objects on python exit, for memory debugging.
level indicates aggressiveness, default 0 releases nothing.
-w, –working
sets the working directory for cython (the directory modules are searched from)
–gdb output debug information for cygdb
-d, –no-docstrings
strip docstrings from the compiled module.
-a, –annotate
produce a colorized html version of the source.
–line-directives
produce #line directives pointing to the .pyx source
–cplus
output a c++ rather than c file.
–embed[=]
generate a main() function that embeds the python interpreter.
-2 compile based on python-2 syntax and code seman tics.
-3 compile based on python-3 syntax and code seman tics.
–fast-fail abort the compilation on the first error
–warning-error, -werror make all warnings into errors
–warning-extra, -wextra enable extra warnings
-x, –directive =
[,>> import pyximport; pyximport.install()
>>> import sum
>>> sum.sum(1,3)

def test(int n):
cdef int a =0
cdef int i
for i in xrange(n):
a+= i
return a
t = time()
test(10000000)
print “total run time:”
print time()-t

[gcc 4.0.2 20051125 (red hat 4.0.2-8)] on linux2
>>> import pyximport; pyximport.install()
>>> import ctest
total run time:
0.00714015960693清单 10. python 测试代码 from time import time
def test(n):
a =0;
for i in xrange(n):
a+= i
return a
t = time()
test(10000000)
print “total run time:”
print time()-t
[root@v5254085f259 test]# python test.py
total run time:
0.971596002579

Posted in 未分类