Python小技巧 -- 性能优化篇

性能优化篇

1. 循环优化

a. 尽量减少循环内的计算,能循环外能实现的逻辑不放在循环内[2.22倍]

#坏的写法
data = [1,2,3,4,5,6,7]
for i in xrange(1000):
    d_len = len(data)  # 可在循环外实现
    k = d_len + i

# 好的写法
data = [1,2,3,4,5,6,7]
d_len = len(data)
for i in xrange(1000):
    k = d_len + i

b. 避免在循环中使用点操作
       原因:每次调用obj.func时,python都会求该方法的值。所以可先用一个变量保存求得的值,避免循环每次求值

# 普通做法
lowerlist = ['this', 'is', 'lowercase']
upperlist = []
for word in lowerlist:
   upperlist append(str.upper(word))

# 优化后做法
lowerlist = ['this', 'is', 'lowercase']
upperlist = []
upper = str.upper
append = upperlist.append
for word in lowerlist:
    append(upper(word))

c. python2用xrange代替range
       原因python2中xrange返回一个迭代器内存占用为常量,range返回一个数组内存占用大。注:python3中xrange不再存在,range返回一个迭代器。

2. 语言优化性能小技巧

a. 列表解析推导式优于for循环append构建 [3.3倍]

# 坏的写法
deamo_list = []
for i in xrange(10000):
    deamo_list.append(i)

# 好的写法
deamo_list = [i for i in xrange(10000)]

c. 字典(dict)查找优于列表(list)
       因为字典为hash表查找复杂度为O(1),list为数组复杂度为O(n)。当存在大量数据频繁查找时,将list转为dict是较好的选择。

# 一般做法
unsupport_items = ['a', 'b', 'is', 'python', 'jason', 'hello', 'hill', 'with', 'phone', 'test', 
'dfdf', 'apple', 'pddf']
candicates = ['is', 'hat', 'new', 'list', 'old', '.']
target_items = []
for item in candicates:
    if item not in unsupport_items:
        target_items.append(item)

# 优化后写法
unsupport_items = ['a', 'b', 'is', 'python', 'jason', 'hello', 'hill', 'with', 'phone', 'test', 
'dfdf', 'apple', 'pddf']
unsupport_items_dict = dict.fromkeys(unsupport_items, True) # list转为dict
candicates = ['is', 'hat', 'new', 'list', 'old', '.']
target_items = []
for item in candicates:
    if item not in unsupport_items_dict:
        target_items.append(item)

d. 集合(set)比列表(list)更适合做与集、交集、差集操作
       set的union、intersection、difference操作比list的迭代要快。

e. 充分利用Lazy if-evaluation特性
       大部分高级语言都支持条件表达式lazy evaluation特性。即if x and y表达式,如果x为false时y表达式值将不再计算。可将概率大、成本低、能快速确定条件表达式值的表达式放在前面,避免对低概率小或高成本表达式的计算。

# 一般做法
items = ['sss', 'bbb', 'ccc', 'tetasdfsdxx.txt', 'tet.txt', 'asdfa.pdf', 'xasdf.php']
txt_file = []
for item in iems:
    if len(item) > 2 && item.endswith('.txt'):
        txt_file.append(item)

# 好的写法
items = ['sss', 'bbb', 'ccc', 'tetasdfsdxx.txt', 'tet.txt', 'asdfa.pdf', 'xasdf.php']
txt_file = []
for item in iems:
    if item.endswith('.txt') and len(item) > 2:   # 通过判断.txt可快速过滤掉非文本文件,避免了对大量非txt文件名长度的无谓计算
        txt_file.append(item)

f. if done is not None 比语句 if done != None更快

g. python的build in(内建)函数通常比较快
       原因内建函数通常是经过语言特性优化的,性能较好。如:add(a, b)要优于 a + b

h. 使用关键字排序
       使用关键字和默认的sort()方法排序,要优于自定义一个排序函数

# 例子:list
import operator
somelist = [(1, 5, 8), (6, 4, 5), (7, 3, 11)]
somelist.sort(key=operator.itemgetter(0))   # 按照第一个元素排序
somelist.sort(key=operator.itemgetter(1))   # 按照第二个元素排序
somelist.sort(key=operator.itemgetter(1))   # 按照第三个元素排序

# 例子:dict
import operator
somelist = [dict(a=1, b=2), dict(a=3,b=5),dict(a=44,b=33)]
somelist.sort(key=operator.attrgetter('a'))    # 按照关键字'a'排序
somelist.sort(key=operator.attrgetter('b'))    # 按照关键字'b'排序

i. 列表推导式
        列表推导式优于for循环

# 普通写法:
alist = []
for i in xrange(10):
    alist.append(i)
# 优化后写法1,[]生成一个列表:
alist = [i for i in xrange(10)]

# 优化后写法2, ()生成一个生成器:
alist_gen = (i for i in xrange(10)) 

# 优化后写法3,将生成器封装成一个方法
def seq_generator(amount):
    return (i for x in xrange(amount))

for i in seq_generator(10):
    print("index : %s" % i)

3. 字符串优化

a. 字符串操作join优于+或+=
       因为python中字符串对象为不可变对象。对字符串的任何操作,如拼接、修改都会产生一个新的字符串对象,即产生持续的内存申请、copy操作,会对python性能有一定的影响。而join操作会先计算总内存量,一次性申请到位然后copy过去。join的内存申请次数少于+或+=,故性能更优。

# 不好的写法
items = ["I", "am", "from", "china"]
item_str = ""
 for item in items:
    item_str += item + " "

# 好的写法
  items = ["I", "am", "from", "china"]
item_str = " ".join(items)
  • b. 当对字符串可以使用正则表达或内置函数来处理时,优先选择内置函数
    如:str.isalpha()、str.isdigit()、str.startswitch(('x', 'xy'))、str.endswith(('x', 'xy'))
# 一般做法
import re
is_digit_reg = re.compile("^[0-9]$")
data_str = "123"
if is_digit_reg(data_str):
    print("%s is digit" % data_str)

# 优化后做法:
data_str  = "123"
if data_str.isdigit():
    print("%s is digit" % data_str)
  • c. 对字符进行各耍比直接串联读取要快
    原因:字符串的每次操作都会申请内存生成一个临时对象。
# 较好的写法
out_str = "%s from %s at time %s" % (name, city, date)

# 不好的写法
out_str = name + " from " + city + " at time " + date
  • 合理使用生成器或yield
    原因生成器对象所占内存空间大小和列表大小无关,故效率会较高些

4. 关键代码依赖于外部高性能包

       即,利用C、C++等语言编写的喂不包,提高应用程序关键任务的性能。 但这些包通常不支持跨平台,移植性较差。如:Cython、PyInIne(python应用程序中直接使用C代码)、PyPy、Pyrex.
多使用cpython模块.

你可能感兴趣的:(Python小技巧 -- 性能优化篇)