python编码最佳实践之总结

1、数据结构的选择:

     1. 在列表中查找:python

   对于已经排序的列表考虑用bisect模块来实现查找元素,该模块将使用二分查找实现git

def find(seq, el) :
    pos = bisect(seq, el)
    if pos == 0 or ( pos == len(seq) and seq[-1] != el ) :
        return -1
    return pos - 1

    而快速插入一个元素能够用:正则表达式

 bisect.insort(list, element) 

这样就插入元素而且不须要再次调用 sort() 来保序,要知道对于长list代价很高.算法

    2. set代替列表: 编程

    好比要对一个list进行去重,最容易想到的实现:数组

seq = ['a', 'a', 'b']
res = []
for i in seq:
    if i not in res:
        res.append(i)

显然上面的实现的复杂度是O(n2),若改为:数据结构

seq = ['a', 'a', 'b']
res = set(seq)

复杂度立刻降为O(n),固然这里假定set能够知足后续使用。架构

另外,set的union,intersection,difference等操做要比列表的迭代快的多,所以若是涉及到求列表交集,并集或者差集等问题能够转换为set来进行,平时使用的时候多注意下,特别当列表比较大的时候,性能的影响就更大。app

    3. 使用python的collections模块替代内建容器类型:框架

collections有三种类型:

  1. deque:加强功能的相似list类型
  2. defaultdict:相似dict类型
  3. namedtuple:相似tuple类型

 

       列表是基于数组实现的,而deque是基于双链表的,因此后者在中间or前面插入元素,或者删除元素都会快不少。 

       defaultdict为新的键值添加了一个默认的工厂,能够避免编写一个额外的测试来初始化映射条目,比dict.setdefault更高效,引用python文档的一个例子:

#使用profile stats工具进行性能分析

>>> from pbp.scripts.profiler import profile, stats
>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3),
... ('blue', 4), ('red', 1)]
>>> @profile('defaultdict')
... def faster():
... d = defaultdict(list)
... for k, v in s:
... d[k].append(v)
...
>>> @profile('dict')
... def slower():
... d = {}
... for k, v in s:
... d.setdefault(k, []).append(v)
...
>>> slower(); faster()
Optimization: Solutions
[ 306 ]
>>> stats['dict']
{'stones': 16.587882671716077, 'memory': 396,
'time': 0.35166311264038086}
>>> stats['defaultdict']
{'stones': 6.5733464259021686, 'memory': 552,
'time': 0.13935494422912598}

可见性能提高了快3倍。defaultdict用一个list工厂做为参数,一样可用于内建类型,好比long等。

 

除了实现的算法、架构以外,python提倡简单、优雅。因此正确的语法实践又颇有必要,这样才会写出优雅易于阅读的代码。

2、语法最佳实践:

  1. 字符串操做:优于python字符串对象是不可改变的,所以对任何字符串的操做如拼接,修改等都将产生一个新的字符串对象,而不是基于原字符串,所以这种持续的 copy会在必定程度上影响Python的性能:

        (1)用join代替 '+' 操做符,后者有copy开销;

        (2)同时当对字符串可使用正则表达式或者内置函数来处理的时候,选择内置函数。如str.isalpha(),str.isdigit(),str.startswith((‘x’, ‘yz’)),str.endswith((‘x’, ‘yz’))

        (3)字符格式化操做优于直接串联读取:

     str = "%s%s%s%s" % (a, b, c, d)  # efficient
     str = "" + a + b + c + d + ""  # slow

    2. 善用list comprehension(列表解析)  & generator(生成器) & decorators(装饰器),熟悉itertools等模块:

       (1) 列表解析,我以为是python2中最让我印象深入的特性,举例1:

      >>> # the following is not so Pythonic  
      >>> numbers = range(10)
      >>> i = 0 
      >>> evens = [] 
      >>> while i < len(numbers): 
      >>>    if i %2 == 0: evens.append(i) 
      >>>    i += 1 
      >>> [0, 2, 4, 6, 8] 

      >>> # the good way to iterate a range, elegant and efficient
      >>> evens = [ i for i in range(10) if i%2 == 0] 
      >>> [0, 2, 4, 6, 8] 

   举例2:

def _treament(pos, element):
    return '%d: %s' % (pos, element)
f = open('test.txt', 'r')
if __name__ == '__main__':
    #list comps 1
    print sum(len(word) for line in f for word in line.split())
    #list comps 2
    print [(x + 1, y + 1) for x in range(3) for y in range(4)]
    #func
    print filter(lambda x: x % 2 == 0, range(10))
    #list comps3
    print [i for i in range(10) if i % 2 == 0]
    #list comps4 pythonic
    print [_treament(i, el) for i, el in enumerate(range(10))]

output:
24
[(1, 1), (1, 2), (1, 3), (1, 4), (2, 1), (2, 2), (2, 3), (2, 4), (3, 1), (3, 2), (3, 3), (3, 4)]
[0, 2, 4, 6, 8]
[0, 2, 4, 6, 8]
['0: 0', '1: 1', '2: 2', '3: 3', '4: 4', '5: 5', '6: 6', '7: 7', '8: 8', '9: 9']

没错,就是这么优雅简单。

   (2) 生成器表达式在python2.2引入,它使用'lazy evaluation'思想,所以在使用内存上更有效。引用python核心编程中计算文件中最长的行的例子:

 

f = open('/etc/motd, 'r')
longest = max(len(x.strip()) for x in f)
f.close()
return longest

这种实现简洁并且不须要把文件文件全部行读入内存。

       (3) python在2.4引入装饰器,又是一个让人兴奋的特性,简单来讲它使得函数和方法封装(接收一个函数并返回加强版本的函数)更容易阅读、理解。'@'符号是装饰器语法,你能够装饰一个函数,记住调用结果供后续使用,这种技术被称为memoization的,下面是用装饰器完成一个cache功能:

import time
import hashlib
import pickle
from itertools import chain
cache = {}
def is_obsolete(entry, duration):
    return time.time() - entry['time'] > duration

def compute_key(function, args, kw):
    #序列化/反序列化一个对象,这里是用pickle模块对函数和参数对象进行序列化为一个hash值
    key = pickle.dumps((function.func_name, args, kw))
    #hashlib是一个提供MD5和sh1的一个库,该结果保存在一个全局字典中
    return hashlib.sha1(key).hexdigest()

def memoize(duration=10):
    def _memoize(function):
        def __memoize(*args, **kw):
            key = compute_key(function, args, kw)

            # do we have it already
            if (key in cache and
                not is_obsolete(cache[key], duration)):
                print 'we got a winner'
                return cache[key]['value']

            # computing
            result = function(*args, **kw)
            # storing the result
            cache[key] = {'value': result,-
                            'time': time.time()}
            return result
        return __memoize
    return _memoize

@memoize()
def very_very_complex_stuff(a, b, c):
    return a + b + c

print very_very_complex_stuff(2, 2, 2)
print very_very_complex_stuff(2, 2, 2)


@memoize(1)
def very_very_complex_stuff(a, b):
    return a + b

print very_very_complex_stuff(2, 2)
time.sleep(2)
print very_very_complex_stuff(2, 2)
 

运行结果:

6

we got a winner

6

4

4

装饰器在不少场景用到,好比参数检查、锁同步、单元测试框架等,有兴趣的人能够本身进一步学习。

    3.  善用python强大的自省能力(属性和描述符):自从使用了python,真的是惊讶原来自省能够作的这么强大简单,关于这个话题,限于内容比较多,这里就不赘述,后续有时间单独作一个总结,学习python必须对其自省好好理解。

 

3、 编码小技巧:

  1. 在python3以前版本使用xrange代替range,由于range()直接返回完整的元素列表而xrange()在序列中每次调用只产生一个整数元素,开销小。(在python3中xrange再也不存在,里面range提供一个能够 遍历任意长度的范围的iterator)
  2. if done is not None比语句if done != None更快;
  3. 尽可能使用"in"操做符,简洁而快速: for i in seq: print i
  4. 'x < y < z'代替'x < y and y < z';
  5. while 1要比while True更快, 由于前者是单步运算,后者还须要计算;
  6. 尽可能使用build-in的函数,由于这些函数每每很高效,好比add(a,b)要优于a+b;
  7. 在耗时较多的循环中,能够把函数的调用改成内联的方式,内循环应该保持简洁。
  8. 使用多重赋值来swap元素:

          x, y = y, x  # elegant and efficient

             而不是:

          temp = x 
          x = y 
          y = temp 

      9. 三元操做符(python2.5后):V1 if X else V2,避免使用(X and V1) or V2,由于后者当V1=""时,就会有问题。

      10. python之switch case实现:由于switch case语法彻底可用if else代替,因此python就没  有switch case语法,可是咱们能够用dictionary或lamda实现:

switch case结构:
switch (var)
{
    case v1: func1();
    case v2: func2();
    ...
    case vN: funcN();
    default: default_func();
}

dictionary实现:

values = {
           v1: func1,
           v2: func2,
           ...
           vN: funcN,
         }
values.get(var, default_func)()

lambda实现:

{
  '1': lambda: func1,
  '2': lambda: func2,
  '3': lambda: func3
}[value]()

用try…catch来实现带Default的状况,我的推荐使用dict的实现方法。

相关文章
相关标签/搜索