Python編程規範筆記(上)

Python編程規範筆記(上)

寫在前面:

從C語言開始,本身陸續學習了C++/Java,可是自從研究生作畢設接觸Python以來,就愛不釋手,再也沒有動力嘗試其餘語言。然而慚愧的是,因爲一直從事科研工做,由於不多關注代碼規範性與代碼效率,致使本身以前論文所作實驗代碼可讀性極差。故而忙裏抽閒得一半日,粗攬《Python編程之美》,收貨頗豐,故而重作於屏幕前,權當整理記錄。因爲全書於己涉及知識點很多,故而僅挑選當前急用者梳理之。python

1、PEP_8與PEP_20

Python的一大優點就是具有優秀的可讀性,而這基於一套較爲完整的公認編程規範。這裏建議全部初學或使用Python的同窗,能夠先從PEP_8入手,其中包括了命名、空格、換行以及函數參數等多個最經常使用最基本的編程規範。編程

以本身爲例,一般命名變量都採起大小寫組合的形式,如一個表示用戶特徵的變量,一般會命名爲Users_Feats_lst,而閱讀了PEP_8以後才發覺這樣寫十分不美觀,致使讀起來十分混亂,推薦的命名規範是:閉包

  • 常量能夠採用全大寫;
  • 類名能夠採用HelloWorld的單詞首字母大寫形式;
  • 其他函數與變量名,所有推薦採用小寫形式;

上面是本身初讀完PEP_8馬上就用上的一點小規範,感受很實用。所以建議你們在開始Python前抽出十分鐘先讀讀PEP_8/20,後者是對於前者的一個擴充升級,不過我建議初學的同窗先從8開始就能夠了,逐步學習便可。app

PEP8 -- Python代碼樣式指南(中文版)函數


2、通常性建議概括

這部分咱們就開始梳理感受最實用的規範了。學習

2.1 明確勝於隱晦

Python中提供了同一功能的多種編碼實現方式,那麼越明確越簡練的方式就越推薦。判斷代碼寫得是否優雅的一個經驗法則是:其餘開發者是否能夠只閱讀函數的首行與末行就理解函數的做用。編碼

2.2 留白勝於緊湊

Python通常要求一行僅寫一個語義句,避免多步處理寫在同一行;固然,若是一個長參數函數能夠在函數部分斷行,如使用反斜槓**或者小括號。.net

2.3 儘可能僅在一處返回函數結果

當遇到多狀況的if-else判斷時,每每能夠在每一個判斷後返回結果,然而這與具備多重出口的迷宮同樣,會下降可讀性,故而通常要求函數寫成僅有一個返回return的形式。代碼規範


3、約定

3.1 檢查相等性的替代方法

對於和邏輯變量以及0比較的狀況,相等性檢查所有建議替換爲直接使用if語句,以下述狀況就推薦2)的形式。code

1)if flag_0 == True:
    pass
2)if flag_0:
    pass

3.2 操做列表(列表解析與filter/map函數)

通常而言循環操做列表時咱們都喜歡使用如下方法,如從下述列表中篩選出大於等於4的元素:

a = range(10)
b = []
for ele in a:
    if a > 4:
        b.append(ele)
    else:
        continue
print b

然而,如今咱們要推薦一種更爲簡潔優美的方法:列表解析

a = range(10)
b = [i + 2 for i in a if i > 4]
print b
-->[7, 8, 9, 10, 11]

即,咱們只要遵循格式[ele operator | ele from where | ele condation]便可,進一步地,上述代碼結果說明不只能夠設置篩選特定條件的元素,進一步還能夠對每一個元素進行運算。

固然,若是上面的公式你感受彆扭不容易讀,那麼也可使用一樣功能的函數,只是篩選對應filter(),運算對應map(),其中filter函數返回篩選條件爲True的元素組成的新列表,而map函數返回函數操做結果組成的新列表。

c = filter(lambda x : x > 4, a)
print c
-->[5, 6, 7, 8, 9]
d = map(lambda x : x * x, a)
print d
-->0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

3.3 解包

解包操做主要用於已知列表或者元祖長度的狀況下分配元素,其通常通用公式爲:

taget_1, taget_2,...,target_N=長度N的列表或者元祖

須要注意的是,僅在Python 3.0以上版本中,才支持可變目標參數的解包(Unpack)

3.4 建立一個包含N個相同對象的列表

你可能會想這還不簡單?可是其實很容易犯錯,咱們先給出總結的規律:

  • 若非可變對象*N,能夠獲得正確的N個相同對象的列表
  • 對於空對象[]*N,結果依舊是空對象[]
  • 對於可變對象[[]]*N,其結果默認是N個指向同一個列表的對象列表
# case 1
four_nones = [None]
print four_nones * 4
-->[None, None, None, None]
# case 2
four_nones = []
print four_nones * 4
-->[]
# case 3
four_nones = [[]] * 4
print four_nones 
-->[[], [], [], []]
four_nones[0].append(1) # still be list, so save the copy attr
print four_nones
-->[[1], [1], [1], [1]]
four_nones[0] = 2 # changed to be integer not list
print four_nones
-->[2, [1], [1], [1]]
# suggest format: 列表解析
four_nones = [[] for i in range(4)]
four_nones[0].append(100)
print four_nones
-->[[100], [], [], []]

4、常見陷阱

所謂陷阱,天然是容易犯錯的部分,這裏簡單梳理兩例。

4.1 可變的默認參數

有些時候咱們但願定義一個可供調用的函數,其中包含一個可變參數以適應不一樣調用場景。如:

# wrong case:
def append_to(ele, to=[]):
    to.append(ele)
    return to
my_list_1 = append_to(10)
print my_list_1
-->[10]
my_list_2 = append_to(20)
print my_list_2
-->[10, 20]

細心的同窗可能發現了,咱們本但願兩次函數調用分別獲得[10]與[20],結果第二次執行時卻保留了第一次執行的結果,也就是說,雖然咱們使用了可變參數做爲默認參數,致使其並未在後續調用時刷新。Python在函數定義而非調用時自動計算其默認參數。

一個解決的簡單方法是,直接設置一個默認值表示沒有提供默認參數,而是每次從新初始化對象:

def append_to(ele, to=None):
    if to is None:
        to=[]
    to.append(ele)
    return to
my_list_1 = append_to(10)
print my_list_1
-->[10]
my_list_2 = append_to(20)
print my_list_2
-->[20]

4.2 延遲綁定的閉包

有些時候,咱們但願能夠採用循環的方式建立多個惟一性函數,而這時候其中的循環變量帶來的延遲綁定可能致使錯誤。如:

# wrong case:
def create_multipliers():
    return [lambda x : x * i for i in range(5)]
for multiplier in create_multipliers():
    print multiplier(2)
-->8
-->8
-->8
-->8
-->8

本來但願的結果是[0,2,4,6,8],結果卻徹底同樣。其緣由在於for循環依次訪問函數對象列表時,確定函數對象生成函數已經完成,返回的函數對象中i值所有爲4,所以致使結果一致。此時只須要單獨綁定一個默認參數i_0,避免受到i值影響便可:

def create_multipliers():
    return [lambda x, i_0 = i : x * i_0 for i in range(5)]
for multiplier in create_multipliers():
    print multiplier(2)
-->0
-->2
-->4
-->6
-->8

5、小結

今天所梳理的部分集中在兩個方面:

  • 列表解析帶來的列表循環操做的優雅表達法以及建立N個相等對象列表時可能出現的淺複製問題,列表解析屬於深複製;
  • 函數調用過程當中默認參數在定義時初始化而非調用時初始化,且循環建立惟一性函數時容易受到閉包綁定延遲帶來的影響致使程序錯誤;
相關文章
相關標籤/搜索