国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > Python > 正文

python編碼最佳實(shí)踐之總結(jié)

2019-11-25 16:56:25
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

相信用python的同學(xué)不少,本人也一直對(duì)python情有獨(dú)鐘,毫無(wú)疑問(wèn)python作為一門解釋性動(dòng)態(tài)語(yǔ)言沒(méi)有那些編譯型語(yǔ)言高效,但是python簡(jiǎn)潔、易讀以及可擴(kuò)展性等特性使得它大受青睞。

 工作中很多同事都在用python,但往往很少有人關(guān)注它的性能和慣用法,一般都是現(xiàn)學(xué)現(xiàn)用,畢竟python不是我們的主要語(yǔ)言,我們一般只是使用它來(lái)做一些系統(tǒng)管理的工作。但是我們?yōu)槭裁床蛔龅母媚兀縫ython 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鼓勵(lì)使用一種最優(yōu)的方法去完成一件事,這也是和ruby等的一個(gè)差異。所以一種好的python編寫習(xí)慣個(gè)人認(rèn)為很重要,本文就重點(diǎn)從性能角度出發(fā)對(duì)python的一些慣用法做一個(gè)簡(jiǎn)單總結(jié),希望對(duì)大家有用~

    提到性能,最容易想到的是降低復(fù)雜度,一般可以通過(guò)測(cè)量代碼回路復(fù)雜度(cyclomatic complexitly)和Landau符號(hào)(大O)來(lái)分析, 比如dict查找是O(1),而列表的查找卻是O(n),顯然數(shù)據(jù)的存儲(chǔ)方式選擇會(huì)直接影響算法的復(fù)雜度。

一、數(shù)據(jù)結(jié)構(gòu)的選擇
1. 在列表中查找:

 對(duì)于已經(jīng)排序的列表考慮用bisect模塊來(lái)實(shí)現(xiàn)查找元素,該模塊將使用二分查找實(shí)現(xiàn)

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

而快速插入一個(gè)元素可以用:

 bisect.insort(list, element) 

這樣就插入元素并且不需要再次調(diào)用 sort() 來(lái)保序,要知道對(duì)于長(zhǎng)list代價(jià)很高.

2. set代替列表:

 比如要對(duì)一個(gè)list進(jìn)行去重,最容易想到的實(shí)現(xiàn):

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

顯然上面的實(shí)現(xiàn)的復(fù)雜度是O(n2),若改成:

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

復(fù)雜度馬上降為O(n),當(dāng)然這里假定set可以滿足后續(xù)使用。

另外,set的union,intersection,difference等操作要比列表的迭代快的多,因此如果涉及到求列表交集,并集或者差集等問(wèn)題可以轉(zhuǎn)換為set來(lái)進(jìn)行,平時(shí)使用的時(shí)候多注意下,特別當(dāng)列表比較大的時(shí)候,性能的影響就更大。

3. 使用python的collections模塊替代內(nèi)建容器類型:

collections有三種類型:

deque:增強(qiáng)功能的類似list類型
defaultdict:類似dict類型
namedtuple:類似tuple類型

       列表是基于數(shù)組實(shí)現(xiàn)的,而deque是基于雙鏈表的,所以后者在中間or前面插入元素,或者刪除元素都會(huì)快很多。

       defaultdict為新的鍵值添加了一個(gè)默認(rèn)的工廠,可以避免編寫一個(gè)額外的測(cè)試來(lái)初始化映射條目,比dict.setdefault更高效,引用python文檔的一個(gè)例子:

#使用profile stats工具進(jìn)行性能分析>>> 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}

可見(jiàn)性能提升了快3倍。defaultdict用一個(gè)list工廠作為參數(shù),同樣可用于內(nèi)建類型,比如long等。

除了實(shí)現(xiàn)的算法、架構(gòu)之外,python提倡簡(jiǎn)單、優(yōu)雅。所以正確的語(yǔ)法實(shí)踐又很有必要,這樣才會(huì)寫出優(yōu)雅易于閱讀的代碼。

二、語(yǔ)法最佳實(shí)踐
字符串操作:優(yōu)于python字符串對(duì)象是不可改變的,因此對(duì)任何字符串的操作如拼接,修改等都將產(chǎn)生一個(gè)新的字符串對(duì)象,而不是基于原字符串,因此這種持續(xù)的 copy會(huì)在一定程度上影響Python的性能:
        (1)用join代替 '+' 操作符,后者有copy開(kāi)銷;

        (2)同時(shí)當(dāng)對(duì)字符串可以使用正則表達(dá)式或者內(nèi)置函數(shù)來(lái)處理的時(shí)候,選擇內(nèi)置函數(shù)。如str.isalpha(),str.isdigit(),str.startswith((‘x', ‘yz')),str.endswith((‘x', ‘yz'))

        (3)字符格式化操作優(yōu)于直接串聯(lián)讀取:

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

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

(1) 列表解析,我覺(jué)得是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']

沒(méi)錯(cuò),就是這么優(yōu)雅簡(jiǎn)單。

   (2) 生成器表達(dá)式在python2.2引入,它使用'lazy evaluation'思想,因此在使用內(nèi)存上更有效。引用python核心編程中計(jì)算文件中最長(zhǎng)的行的例子:

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

這種實(shí)現(xiàn)簡(jiǎn)潔而且不需要把文件文件所有行讀入內(nèi)存。

 (3) python在2.4引入裝飾器,又是一個(gè)讓人興奮的特性,簡(jiǎn)單來(lái)說(shuō)它使得函數(shù)和方法封裝(接收一個(gè)函數(shù)并返回增強(qiáng)版本的函數(shù))更容易閱讀、理解。'@'符號(hào)是裝飾器語(yǔ)法,你可以裝飾一個(gè)函數(shù),記住調(diào)用結(jié)果供后續(xù)使用,這種技術(shù)被稱為memoization的,下面是用裝飾器完成一個(gè)cache功能:

import timeimport hashlibimport picklefrom itertools import chaincache = {}def is_obsolete(entry, duration):  return time.time() - entry['time'] > durationdef compute_key(function, args, kw):  #序列化/反序列化一個(gè)對(duì)象,這里是用pickle模塊對(duì)函數(shù)和參數(shù)對(duì)象進(jìn)行序列化為一個(gè)hash值  key = pickle.dumps((function.func_name, args, kw))  #hashlib是一個(gè)提供MD5和sh1的一個(gè)庫(kù),該結(jié)果保存在一個(gè)全局字典中  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)

運(yùn)行結(jié)果:

6we got a winner644

裝飾器在很多場(chǎng)景用到,比如參數(shù)檢查、鎖同步、單元測(cè)試框架等,有興趣的人可以自己進(jìn)一步學(xué)習(xí)。

3.  善用python強(qiáng)大的自省能力(屬性和描述符):自從使用了python,真的是驚訝原來(lái)自省可以做的這么強(qiáng)大簡(jiǎn)單,關(guān)于這個(gè)話題,限于內(nèi)容比較多,這里就不贅述,后續(xù)有時(shí)間單獨(dú)做一個(gè)總結(jié),學(xué)習(xí)python必須對(duì)其自省好好理解。

三、 編碼小技巧
1、在python3之前版本使用xrange代替range,因?yàn)閞ange()直接返回完整的元素列表而xrange()在序列中每次調(diào)用只產(chǎn)生一個(gè)整數(shù)元素,開(kāi)銷小。(在python3中xrange不再存在,里面range提供一個(gè)可以 遍歷任意長(zhǎng)度的范圍的iterator)
2、if done is not None比語(yǔ)句if done != None更快;
3、盡量使用"in"操作符,簡(jiǎn)潔而快速: for i in seq: print i
4、'x < y < z'代替'x < y and y < z';
5、while 1要比while True更快, 因?yàn)榍罢呤菃尾竭\(yùn)算,后者還需要計(jì)算;
6、盡量使用build-in的函數(shù),因?yàn)檫@些函數(shù)往往很高效,比如add(a,b)要優(yōu)于a+b;
7、在耗時(shí)較多的循環(huán)中,可以把函數(shù)的調(diào)用改為內(nèi)聯(lián)的方式,內(nèi)循環(huán)應(yīng)該保持簡(jiǎn)潔。
8、使用多重賦值來(lái)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,因?yàn)楹笳弋?dāng)V1=""時(shí),就會(huì)有問(wèn)題。

10. python之switch case實(shí)現(xiàn):因?yàn)閟witch case語(yǔ)法完全可用if else代替,所以python就沒(méi)  有switch case語(yǔ)法,但是我們可以用dictionary或lamda實(shí)現(xiàn):

switch case結(jié)構(gòu):

switch (var){  case v1: func1();  case v2: func2();  ...  case vN: funcN();  default: default_func();}dictionary實(shí)現(xiàn):values = {      v1: func1,      v2: func2,      ...      vN: funcN,     }values.get(var, default_func)()lambda實(shí)現(xiàn):{ '1': lambda: func1, '2': lambda: func2, '3': lambda: func3}[value]()

用try…catch來(lái)實(shí)現(xiàn)帶Default的情況,個(gè)人推薦使用dict的實(shí)現(xiàn)方法。

 這里只總結(jié)了一部分python的實(shí)踐方法,希望這些建議可以幫助到每一位使用python的同學(xué),優(yōu)化性能不是重點(diǎn),高效解決問(wèn)題,讓自己寫的代碼更加易于維護(hù)!

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 鄄城县| 临沂市| 巨野县| 高唐县| 日喀则市| 徐汇区| 鹿邑县| 江山市| 新疆| 阿拉善左旗| 沂水县| 重庆市| 阿城市| 莱州市| 铁力市| 永胜县| 清涧县| 京山县| 辽源市| 海丰县| 邵东县| 台山市| 黄浦区| 桐城市| 九江市| 阳朔县| 江城| 德兴市| 旌德县| 东莞市| 永胜县| 诸城市| 北京市| 麻阳| 湖北省| 留坝县| 方正县| 左权县| 大渡口区| 涿州市| 汪清县|