代碼這樣寫更優雅 (Python 版)
Python 這門語言最大的優點之一就是語法簡潔,好的代碼就像偽代碼一樣,乾淨、整潔、一目了然。但有時候我們寫代碼,特別是 Python 初學者,往往還是按照其它語言的思維習慣來寫,那樣的寫法不僅運行速度慢,代碼讀起來也費盡,給人一種拖泥帶水的感覺,過段時間連自己也讀不懂。
《計算機程序的構造和解釋》的作者哈爾·阿伯爾森曾這樣說:「Programs must be written for people to read, and only incidentally for machines to execute.」
要寫出 Pythonic(優雅的、地道的、整潔的)代碼,還要平時多觀察那些大牛代碼,Github 上有很多非常優秀的源代碼值得閱讀,比如:requests、flask、tornado,筆者列舉一些常見的 Pythonic 寫法,希望能給你帶來一點啟迪。
1、變數交換
大部分編程語言中交換兩個變數的值時,不得不引入一個臨時變數:
>>> a = 1n>>> b = 2n>>> tmp = an>>> a = bn>>> b = tmpn
pythonic
>>> a, b = b, an
2、循環遍歷區間元素
for i in [0, 1, 2, 3, 4, 5]:n print in# 或者nfor i in range(6):n print (i)n
pythonic
for i in xrange(6):n print (i)n
xrange 返回的是生成器對象,生成器比列表更加節省內存,不過需要注意的是 xrange 是 python2 中的寫法,python3 只有 range 方法,特點和 xrange 是一樣的。
3、帶有索引位置的集合遍歷
遍歷集合時如果需要使用到集合的索引位置時,直接對集合迭代是沒有索引信息的,普通的方式使用:
colors = [red, green, blue, yellow]nnfor i in range(len(colors)):n print (i, --->, colors[i])n
pythonic
for i, color in enumerate(colors):n print (i, --->, color)n
4、字元串連接
字元串連接時,普通的方式可以用 + 操作
names = [raymond, rachel, matthew, roger,n betty, melissa, judith, charlie]nns = names[0]nfor name in names[1:]:n s += , + namenprint (s)n
pythonic
print (, .join(names))n
join 是一種更加高效的字元串連接方式,使用 + 操作時,每執行一次+操作就會導致在內存中生成一個新的字元串對象,遍歷8次有8個字元串生成,造成無謂的內存浪費。而用 join 方法整個過程只會產生一個字元串對象。
5、打開/關閉文件
執行文件操作時,最後一定不能忘記的操作是關閉文件,即使報錯了也要 close。普通的方式是在 finnally 塊中顯示的調用 close 方法。
f = open(data.txt)ntry:n data = f.read()nfinally:n f.close()n
pythonic
with open(data.txt) as f:n data = f.read()n
使用 with 語句,系統會在執行完文件操作後自動關閉文件對象。
6、列表推導式
能夠用一行代碼簡明扼要地解決問題時,絕不要用兩行,比如
result = []nfor i in range(10):n s = i*2n result.append(s)n
pythonic
[i*2 for i in xrange(10)]n
與之類似的還有生成器表達式、字典推導式,都是很 pythonic 的寫法。
7、善用裝飾器
裝飾器可以把與業務邏輯無關的代碼抽離出來,讓代碼保持乾淨清爽,而且裝飾器還能被多個地方重複利用。比如一個爬蟲網頁的函數,如果該 URL 曾經被爬過就直接從緩存中獲取,否則爬下來之後加入到緩存,防止後續重複爬取。
def web_lookup(url, saved={}):n if url in saved:n return saved[url]n page = urllib.urlopen(url).read()n saved[url] = pagen return pagen
pythonic
import urllib #py2n#import urllib.request as urllib # py3nndef cache(func):n saved = {}nn def wrapper(url):n if url in saved:n return saved[url]n else:n page = func(url)n saved[url] = pagen return pagenn return wrappernnn@cachendef web_lookup(url):n return urllib.urlopen(url).read()n
用裝飾器寫代碼表面上感覺代碼量更多,但是它把緩存相關的邏輯抽離出來了,可以給更多的函數調用,這樣總的代碼量就會少很多,而且業務方法看起來簡潔了。
8、合理使用列表
列表對象(list)是一個查詢效率高於更新操作的數據結構,比如刪除一個元素和插入一個元素時執行效率就非常低,因為還要對剩下的元素進行移動
names = [raymond, rachel, matthew, roger,n betty, melissa, judith, charlie]nnames.pop(0)nnames.insert(0, mark)n
pythonic
from collections import dequennames = deque([raymond, rachel, matthew, roger,n betty, melissa, judith, charlie])nnames.popleft()nnames.appendleft(mark)n
deque 是一個雙向隊列的數據結構,刪除元素和插入元素會很快
9、序列解包
p = vttalk, female, 30, python@qq.comnnname = p[0]ngender = p[1]nage = p[2]nemail = p[3]n
pythonic
name, gender, age, email = pn
10、遍歷字典的 key 和 value
方法一速度沒那麼快,因為每次迭代的時候還要重新進行hash查找 key 對應的 value。
方法二遇到字典非常大的時候,會導致內存的消耗增加一倍以上
# 方法一nfor k in d:n print k, --->, d[k]nn# 方法二nfor k, v in d.items():n print (k, --->, v)n
pythonic
for k, v in d.iteritems():n print (k, --->, v)n
iteritems 返回迭代器對象,可節省更多的內存,不過在 python3 中沒有該方法了,只有 items 方法,等值於 iteritems。
當然還有很多 pythonic 寫法,在此不再一一列舉,說不定有第二期,歡迎留言。覺得不錯就贊一個吧 (^o^)/
公眾號『Python之禪』(id:VTtalk),分享 Python 等技術乾貨和有溫度的內容,歡迎關注
博客地址:代碼這樣寫更優雅(Python版) - FooFish
推薦閱讀:
TAG:Python |