首頁 後端開發 Python教學 python编码最佳实践之总结

python编码最佳实践之总结

Jun 10, 2016 pm 03:06 PM
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
登入後複製

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

 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 < 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必须对其自省好好理解。

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

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

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆樹的耳語 - 如何解鎖抓鉤
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1669
14
CakePHP 教程
1428
52
Laravel 教程
1329
25
PHP教程
1273
29
C# 教程
1256
24
PHP和Python:解釋了不同的範例 PHP和Python:解釋了不同的範例 Apr 18, 2025 am 12:26 AM

PHP主要是過程式編程,但也支持面向對象編程(OOP);Python支持多種範式,包括OOP、函數式和過程式編程。 PHP適合web開發,Python適用於多種應用,如數據分析和機器學習。

在PHP和Python之間進行選擇:指南 在PHP和Python之間進行選擇:指南 Apr 18, 2025 am 12:24 AM

PHP適合網頁開發和快速原型開發,Python適用於數據科學和機器學習。 1.PHP用於動態網頁開發,語法簡單,適合快速開發。 2.Python語法簡潔,適用於多領域,庫生態系統強大。

sublime怎麼運行代碼python sublime怎麼運行代碼python Apr 16, 2025 am 08:48 AM

在 Sublime Text 中運行 Python 代碼,需先安裝 Python 插件,再創建 .py 文件並編寫代碼,最後按 Ctrl B 運行代碼,輸出會在控制台中顯示。

PHP和Python:深入了解他們的歷史 PHP和Python:深入了解他們的歷史 Apr 18, 2025 am 12:25 AM

PHP起源於1994年,由RasmusLerdorf開發,最初用於跟踪網站訪問者,逐漸演變為服務器端腳本語言,廣泛應用於網頁開發。 Python由GuidovanRossum於1980年代末開發,1991年首次發布,強調代碼可讀性和簡潔性,適用於科學計算、數據分析等領域。

Python vs. JavaScript:學習曲線和易用性 Python vs. JavaScript:學習曲線和易用性 Apr 16, 2025 am 12:12 AM

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

Golang vs. Python:性能和可伸縮性 Golang vs. Python:性能和可伸縮性 Apr 19, 2025 am 12:18 AM

Golang在性能和可擴展性方面優於Python。 1)Golang的編譯型特性和高效並發模型使其在高並發場景下表現出色。 2)Python作為解釋型語言,執行速度較慢,但通過工具如Cython可優化性能。

vscode在哪寫代碼 vscode在哪寫代碼 Apr 15, 2025 pm 09:54 PM

在 Visual Studio Code(VSCode)中編寫代碼簡單易行,只需安裝 VSCode、創建項目、選擇語言、創建文件、編寫代碼、保存並運行即可。 VSCode 的優點包括跨平台、免費開源、強大功能、擴展豐富,以及輕量快速。

notepad 怎麼運行python notepad 怎麼運行python Apr 16, 2025 pm 07:33 PM

在 Notepad 中運行 Python 代碼需要安裝 Python 可執行文件和 NppExec 插件。安裝 Python 並為其添加 PATH 後,在 NppExec 插件中配置命令為“python”、參數為“{CURRENT_DIRECTORY}{FILE_NAME}”,即可在 Notepad 中通過快捷鍵“F6”運行 Python 代碼。

See all articles