程式碼這樣寫更優雅 (Python 版)

劉志軍發表於2017-03-01

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 = 1
>>> b = 2
>>> tmp = a
>>> a = b
>>> b = tmp複製程式碼

pythonic

>>> a, b = b, a複製程式碼

2、迴圈遍歷區間元素

for i in [0, 1, 2, 3, 4, 5]:
    print i2
# 或者
for i in range(6):
    print i2複製程式碼

pythonic

for i in xrange(6):
    print i2複製程式碼

xrange 返回的是生成器物件,生成器比列表更加節省記憶體,不過需要注意的是 xrange 是 python2 中的寫法,python3 只有 range 方法,特點和 xrange 是一樣的。

3、帶有索引位置的集合遍歷

遍歷集合時如果需要使用到集合的索引位置時,直接對集合迭代是沒有索引資訊的,普通的方式使用:

colors = ['red', 'green', 'blue', 'yellow']

for i in range(len(colors)):
    print i, '--->', colors[i]複製程式碼

pythonic

for i, color in enumerate(colors):
    print i, '--->', color複製程式碼

4、字串連線

字串連線時,普通的方式可以用 + 操作

names = ['raymond', 'rachel', 'matthew', 'roger',
         'betty', 'melissa', 'judith', 'charlie']

s = names[0]
for name in names[1:]:
    s += ', ' + name
print s複製程式碼

pythonic

print ', '.join(names)複製程式碼

join 是一種更加高效的字串連線方式,使用 + 操作時,每執行一次+操作就會導致在記憶體中生成一個新的字串物件,遍歷8次有8個字串生成,造成無謂的記憶體浪費。而用 join 方法整個過程只會產生一個字串物件。

5、開啟/關閉檔案

執行檔案操作時,最後一定不能忘記的操作是關閉檔案,即使報錯了也要 close。普通的方式是在 finnally 塊中顯示的呼叫 close 方法。

f = open('data.txt')
try:
    data = f.read()
finally:
    f.close()複製程式碼

pythonic

with open('data.txt') as f:
    data = f.read()複製程式碼

使用 with 語句,系統會在執行完檔案操作後自動關閉檔案物件。

6、列表推導式

能夠用一行程式碼簡明扼要地解決問題時,絕不要用兩行,比如

result = []
for i in range(10):
    s = i  2
    result.append(s)複製程式碼

pythonic

[i2 for i in xrange(10)]複製程式碼

與之類似的還有生成器表示式、字典推導式,都是很 pythonic 的寫法。

7、善用裝飾器

裝飾器可以把與業務邏輯無關的程式碼抽離出來,讓程式碼保持乾淨清爽,而且裝飾器還能被多個地方重複利用。比如一個爬蟲網頁的函式,如果該 URL 曾經被爬過就直接從快取中獲取,否則爬下來之後加入到快取,防止後續重複爬取。

def web_lookup(url, saved={}):
    if url in saved:
        return saved[url]
    page = urllib.urlopen(url).read()
    saved[url] = page
    return page複製程式碼

pythonic


import urllib #py2
#import urllib.request as urllib # py3

def cache(func):
    saved = {}

    def wrapper(url):
        if url in saved:
            return saved[url]
        else:
            page = func(url)
            saved[url] = page
            return page

    return wrapper


@cache
def web_lookup(url):
    return urllib.urlopen(url).read()複製程式碼

用裝飾器寫程式碼表面上感覺程式碼量更多,但是它把快取相關的邏輯抽離出來了,可以給更多的函式呼叫,這樣總的程式碼量就會少很多,而且業務方法看起來簡潔了。

8、合理使用列表

列表物件(list)是一個查詢效率高於更新操作的資料結構,比如刪除一個元素和插入一個元素時執行效率就非常低,因為還要對剩下的元素進行移動

names = ['raymond', 'rachel', 'matthew', 'roger',
         'betty', 'melissa', 'judith', 'charlie']
names.pop(0)
names.insert(0, 'mark')複製程式碼

pythonic

from collections import deque
names = deque(['raymond', 'rachel', 'matthew', 'roger',
               'betty', 'melissa', 'judith', 'charlie'])
names.popleft()
names.appendleft('mark')複製程式碼

deque 是一個雙向佇列的資料結構,刪除元素和插入元素會很快

9、序列解包

p = 'vttalk', 'female', 30, 'python@qq.com'

name = p[0]
gender = p[1]
age = p[2]
email = p[3]複製程式碼

pythonic

name, gender, age, email = p複製程式碼

10、遍歷字典的 key 和 value

方法一速度沒那麼快,因為每次迭代的時候還要重新進行hash查詢 key 對應的 value。

方法二遇到字典非常大的時候,會導致記憶體的消耗增加一倍以上

# 方法一
for k in d:
    print k, '--->', d[k]

# 方法二
for k, v in d.items():
    print k, '--->', v複製程式碼

pythonic

for k, v in d.iteritems():
    print k, '--->', v複製程式碼

iteritems 返回迭代器物件,可節省更多的記憶體,不過在 python3 中沒有該方法了,只有 items 方法,等值於 iteritems。

​當然還有很多 pythonic 寫法,在此不再一一列舉,說不定有第二期,歡迎留言。覺得不錯就贊一個吧 (^o^)/

公眾號『一個程式設計師的微站』(id:VTtalk),分享 Python 等技術乾貨和有溫度的內容,歡迎關注
部落格地址:foofish.net/idiomatic_p…

程式碼這樣寫更優雅 (Python 版)
一個程式設計師的微站

相關文章