python编码最佳实践之总结

相信用python的同学不少,本人也一直对python情有独钟,毫无疑问python作为一门解释性动态语言没有那些编译型语言高效,但是python简洁、易读以及可扩展性等特性使得它大受青睐。

 工作中很多同事都在用python,但往往很少有人关注它的性能和惯用法,一般都是现学现用,毕竟python不是我们的主要语言,我们一般只是使用它来做一些系统管理的工作。但是我们为什么不做的更好呢?python zen中有这样一句:There should be one– and preferably only one –obvious way to do it. Although that way may not be obvious at first unless you’re Dutch. 大意就是python鼓励使用一种最优的方法去完成一件事,这也是和ruby等的一个差异。所以一种好的python编写习惯个人认为很重要,本文就重点从性能角度出发对python的一些惯用法做一个简单总结,希望对大家有用~

    提到性能,最容易想到的是降低复杂度,一般可以通过测量代码回路复杂度(cyclomatic complexitly)和Landau符号(大O)来分析, 比如dict查找是O(1),而列表的查找却是O(n),显然数据的存储方式选择会直接影响算法的复杂度。

一、数据结构的选择
1. 在列表中查找:

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

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

登录后复制

而快速插入一个元素可以用:

立即学习“Python免费学习笔记(深入)”;

 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来进行,平时使用的时候多注意下,特别当列表比较大的时候,性能的影响就更大。

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

collections有三种类型:

deque:增强功能的类似list类型
defaultdict:类似dict类型
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提倡简单、优雅。所以正确的语法实践又很有必要,这样才会写出优雅易于阅读的代码。

二、语法最佳实践
字符串操作:优于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 >>  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 timeimport hashlibimport picklefrom itertools import chaincache = {}def is_obsolete(entry, duration):  return time.time() - entry['time'] > durationdef 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 + cprint 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 + bprint very_very_complex_stuff(2, 2)time.sleep(2)print very_very_complex_stuff(2, 2)

登录后复制

运行结果:

6we got a winner644

登录后复制

装饰器在很多场景用到,比如参数检查、锁同步、单元测试框架等,有兴趣的人可以自己进一步学习。

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

三、 编码小技巧
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 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的实现方法。

 这里只总结了一部分python的实践方法,希望这些建议可以帮助到每一位使用python的同学,优化性能不是重点,高效解决问题,让自己写的代码更加易于维护!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2535719.html

(0)
上一篇 2025年3月5日 23:04:02
下一篇 2025年3月5日 23:04:08

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • Python在Console下显示文本进度条的方法

    进度条实现原理 进度条和一般的print区别在哪里呢?答案就是print会输出一个\n,也就是换行符,这样光标移动到了下一行行首,接着输出,之前已经通过stdout输出的东西依旧保留,而且保证我们在下面看到最新的输出结果。进度条不然,我们必…

    编程技术 2025年3月5日
    200
  • Python的GUI框架PySide的安装配置教程

    (一)说在前面     Python自带了GUI模块Tkinter,只是界面风格有些老旧。另外就是各种GUI框架了。     之前安装过WxPython,并做了简单的界面。遂最近又重新搜索了一下网上关于Python GUI框架的问题,发现还…

    2025年3月5日
    200
  • 学习python之编写简单乘法口诀表实现代码

    实现代码一、 #!/usr/bin/pythonx,y=9,9 lst=[(x,y,str(y)+’X’+str(x)+’=’+str(x*y)) for x in range(1,y+1) for y in range(1,x+1)]fo…

    编程技术 2025年3月5日
    200
  • 轻松实现python搭建微信公众平台

    本文主要是一步一步教大家如何利用python搭建微信公众平台,有兴趣的朋友可以参考一下 使用的工具,python 新浪SAE平台,微信的公众平台 你需要先在微信的公众平台与新浪SAE平台上各种注册,微信平台注册的时候需要你拍张手持身份证的照…

    2025年3月5日 编程技术
    200
  • 十条建议帮你提高Python编程效率

    程序员的时间很宝贵,python这门语言虽然足够简单、优雅,但并不是说你使用python编程,效率就一定会高。要想节省时间、提高效率,还是需要注意很多地方的。 今天就与大家分享资深Python程序员总结的10点建议,帮助大家大幅节省开发时间…

    2025年3月5日
    200
  • Python批量创建迅雷任务及创建多个文件

    其实不是真的创建了批量任务,而是用python创建一个文本文件,每行一个要下载的链接,然后打开迅雷,复制文本文件的内容,迅雷监测到剪切板变化,弹出下载全部链接的对话框~~ 实际情况是这样的,因为用python分析网页非常,比如下载某页中的全…

    编程技术 2025年3月5日
    200
  • bpython 功能强大的Python shell

    python是一个非常实用、流行的解释型编程语言,其优势之一就是可以借助其交互的shell进行探索式地编程。你可以试着输入一些代码,然后马上获得解释器的反馈,而不必专门写一个脚本。但是python自带的shell也有一些局限性,例如无法自动…

    2025年3月5日
    200
  • python的else子句使用指南

    在日常coding中,分支语句的使用非常普遍,经常会根据是否满足一定的条件对代码执行的逻辑进行一些控制,所以大家对if[elif[else]]一定不会陌生。分支语句中的else子句在其它的条件不满足的时候会被执行到,适当的使用分支语句能够让…

    编程技术 2025年3月5日
    200
  • Python 中 Meta Classes详解

    接触过 django 的同学都应该十分熟悉它的 orm 系统。对于 python 新手而言,这是一项几乎可以被称作“黑科技”的特性:只要你在models.py中随便定义一个model的子类,django 便可以: 获取它的字段定义,并转换成…

    编程技术 2025年3月5日
    200
  • 深入解读Python解析XML的几种方式

    在xml解析方面,python贯彻了自己“开箱即用”(batteries included)的原则。在自带的标准库中,python提供了大量可以用于处理xml语言的包和工具,数量之多,甚至让python编程新手无从选择。 本文将介绍深入解读…

    2025年3月5日 编程技术
    200

发表回复

登录后才能评论