Python 編碼風格參考

代碼除了用來運行外,更多的是用來讀。爲了是代碼的可讀性更強,不少編程語言都有本身的編碼規範。規範的制定是爲了保持代碼的一致性,以使代碼更美觀和易讀。代碼應該怎麼樣排版和編寫並非絕對的,因此一些地方會有爭議。有時風格指南並不適用,最重要的知道什麼時候不一致。當你沒法判斷該怎麼作時,應該所參考下其餘的例子。html

本文僅是一個 Python 編碼風格的參考,並非一個規定,規定必需要這麼去作。本文的目的應該是起一個指導做用,指導開發者去寫更易讀的代碼。python

1、代碼編排

主要是縮進與空行的排版:程序員

  • 一、使用 4 個空格進行縮進(編輯器均可以完成此功能),不推薦使用製表符,更不能混合使用製表符和空格。
  • 二、每行不超過 80 個字符,換行可使用反斜槓,最好使用括號(Python 會將圓括號, 中括號和花括號中的行隱式的鏈接起來)。
  • 三、類和頂層函數定義之間空兩行;類中的方法定義之間空一行;函數內邏輯無關段落之間空一行;其餘地方儘可能不要再空行。

2、文檔編排

主要是整個源碼文件的佈局:算法

  • 一、模塊內容的順序:模塊說明,模塊文檔字符串,導入語句,全局變量或者常量,其餘定義。
  • 二、模塊導入部分順序:標準庫,第三方模塊,自定義模塊;各部分之間空一行。
  • 三、不要在一個 import 語句中一次導入多個模塊,好比 import os, sys 不推薦。
  • 四、導入模塊時應該使用合適的方式來避免命名衝突,例如在適當的時候才使用 from xx import xx,儘可能避免使用 from xx imoprt *
  • 五、在自已編寫的模塊中,若是須要使用 from xx import * 時,應該在導入語句後或者模塊尾使用 __all__ 機制來限制導入規則。

3、語句編排

  • 一、一般每一個語句應該獨佔一行。
  • 二、不要在行尾加分號, 也不要用分號將多條語句放在同一行。
  • 三、if/for/while 語句中,即便執行語句只有一句,也應儘可能另起一行。
  • 四、不要在返回語句(return)或條件語句(if/for/while)中使用括號,除非是用於實現行鏈接。
  • 五、對於 if 語句, 在沒有 else 且語句比較短時,能夠在一行完成(但不推薦),好比:if foo: bar(foo).
  • 六、對於簡單的類定義,也能夠在一行完成(但不推薦),好比定義一個異常:class UnfoundError(Exception): pass.
  • 七、函數和方法的括號中使用垂直隱式縮進或使用懸掛縮進。
# 一行寫不下時,有括號來鏈接多行,後續行應該使用懸掛縮進
if (this_is_one_thing
        and that_is_another_thing):
    do_something()

# 函數調用參數較多時,對準左括號
f = foo(a, b,
        c, d)

# 不對準左括號,但加多一層縮進,以和後面內容區別
def long_function_name(
        a, b, c,
        d, e):
    print(a, b, c, d, e)

# 列表、元組、字典以及函數調用時可讓右括號回退,這樣更加美觀
l = [
    1, 2, 3,
    4, 5, 6,
]

result = some_function(
    'a', 'b', 'c',
    'd', 'e', 'f',
)

4、空格使用

整體原則,避免沒必要要的空格。編程

  • 一、各類右括號前不要加空格。
  • 二、逗號、冒號、分號前不要加空格,但應該在它們後面加(除了在行尾)。
  • 三、函數的左括號前不要加空格。如 Func(1)。
  • 四、序列的左括號前不要加空格。如 list[2]。
  • 五、操做符左右各加一個空格,不要爲了對齊增長空格。
  • 六、函數默認參數使用的賦值符左右省略空格。

良好的風格:session

spam(ham[1], {eggs: 2})

if x == 4:
    print x, y; x, y = y, x

f = foo(1, 2, 3)

ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:]
ham[lower:upper], ham[lower:upper:], ham[lower::step]
ham[lower+offset : upper+offset]
ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
ham[lower + offset : upper + offset]

x = 1
y = 2
long_variable = 3

def foo(a, b, c=0):
    return moo(m=a, n=b, o=c)

很差的風格:數據結構

spam( ham[ 1 ], { eggs: 2 } )

if x == 4 :
    print x , y ; x , y = y , x

f = foo (1, 2, 3)

ham[lower + offset:upper + offset]
ham[1: 9], ham[1 :9], ham[1:9 :3]
ham[lower : : upper]
ham[ : upper]

x             = 1
y             = 2
long_variable = 3

def foo(a, b, c = 0):
    return moo(m = a, n = b, o = c)

5、註釋

整體原則,錯誤的註釋不如沒有註釋。因此當一段代碼發生變化時,第一件事就是要修改註釋。註釋儘可能使用英文,最好是完整的句子,首字母大寫,句後要有結束符,結束符後跟兩個空格,開始下一句。若是是短語,能夠省略結束符。註釋應該在 # 後加一個空格纔開始寫註釋內容。編程語言

  • 一、塊註釋,在一段代碼前增長的註釋。段落之間用只有 ‘#’ 的行間隔。好比:
# Description : Module config.
#
# Input : None
#
# Output : None
  • 二、行註釋,在一句代碼後加註釋。應該儘可能在語句後空兩格後再開始註釋。當有連續的行註釋時,爲了美觀可讓 ‘#’ 對齊。 在語句比較長時,應該儘可能少使用行註釋。好比:
person = {
    "name": "huoty",  # 姓名
    "age": 26,        # 年齡
    "stature": 169,   # 身高
    "weight": 60,     # 體重
}

print person  # 輸出信息
  • 三、對類或者函數的說明,儘可能不要在其定義的前一行或者後一行用塊註釋的形式來講明,而應該使用文檔字符串(docstring)
  • 四、使用 TODO 註釋來標記待完成的工做,團隊協做中,必要的時候應該寫上你的名字或者聯繫方式,好比:
# TODO(sudohuoty@gmail.com): Use a "*" here for string repetition.
# TODO(Huoty) Change this to use relations.
# 你可能會認爲你讀得懂如下的代碼。可是你不會懂的,相信我吧。  

# 要是你嘗試玩弄這段代碼的話,你將會在無盡的通宵中不斷地咒罵本身爲何會認爲本身聰明到能夠優化這段代碼。  
# so,如今請關閉這個文件去玩點別的吧。  

# 程序員1(於2010年6月7日):在這個坑臨時加入一些調料  
# 程序員2(於2011年5月22日):臨你個屁啊  
# 程序員3(於2012年7月23日):樓上都是狗屎,鑑定完畢  
# 程序員4(於2013年8月2日):fuck 樓上,三年了,這坑還在!!!  
# 程序員5(於2014年8月21日):哈哈哈,這坑竟然坑了這麼多人,幸虧我也不用填了,系統終止運行了,you're died

6、文檔描述

  • 一、儘可能爲全部的共有模塊、函數、類、方法寫 docstring
  • 二、前三引號後不該該換行,應該緊接着在後面歸納性的說明模塊、函數、類、方法的做用,而後再空一行進行詳細的說明。後三引號應該單獨佔一行。好比:
"""Convert an API path to a filesystem path

If given, root will be prepended to the path.
root must be a filesystem path already.
"""
  • 二、函數和方法的 docstring 層次順序大體爲概述、詳細描述、參數、返回值、異常,通常不要求描述實現細節,除非其中涉及很是複雜的算法。大體的層次結構以下所示:
"""函數或方法的概述

詳細的描述信息……
詳細的描述信息……

參數說明
--------
    參數1:...
    參數2:...

返回值:
    ...

異常:
    異常1:...
    異常2:...
"""

一個參考示例:編輯器

"""Start a kernel for a session and return its kernel_id.                                                                                             

Parameters
----------
kernel_id : uuid
    The uuid to associate the new kernel with. If this
    is not None, this kernel will be persistent whenever it is
    requested.
path : API path
    The API path (unicode, '/' delimited) for the cwd.
    Will be transformed to an OS path relative to root_dir.
kernel_name : str
    The name identifying which kernel spec to launch. This is ignored if
    an existing kernel is returned, but it may be checked in the future.

Return a kernel id
"""
  • 三、類的 docstring 的層次順序大體爲概述、詳細描述、屬性說明。若是類有公開屬性值時,應該儘可能在 docstring 中進行說明。以下所示:
"""這裏是類的概述。

詳細的描述信息……
詳細的描述信息……

屬性(Attributes):
-----------------
    屬性1: ...
    屬性2: ...
"""

7、命名規範

  • 一、模塊命名儘可能短小,使用所有小寫的方式,可使用下劃線。
  • 二、包命名儘可能短小,使用所有小寫的方式,不可使用下劃線。
  • 三、類的命名使用駝峯命令的方式,即單詞首字符大寫,類名應該所有使用名詞。
  • 四、異常命令應該使用加 Error 後綴的方式,好比:HTTPError。
  • 五、全局變量儘可能只在模塊內有效,而且應該儘可能避免使用全局變量。
  • 六、函數命名使用所有小寫的方式,使用下劃線分割單詞,並採用動賓結構。
  • 七、常量命名使用所有大寫的方式,使用下劃線分割單詞。
  • 八、類的屬性(方法和變量)命名使用所有小寫的方式,使用下劃線分割單詞。
  • 九、變量、類屬性等命令儘可能不要使用縮寫形式,除了計數器和迭代器,儘可能不要使用單字符名稱。
  • 十、類的方法第一個參數必須是 self,而靜態方法第一個參數必須是 cls。
  • 十一、在模塊中要表示私有變量或者函數時,能夠在變量或者函數前加一個下劃線 _foo, _show_msg 來進行訪問控制。
  • 十二、在 Python 中沒有諸如 public、private、protected 等修飾符,而在類的定義中每每會有相似這樣的需求,那麼能夠在屬性或者方法前加一個下劃線表示 protected,加兩個下劃線來表示 private。加兩個下劃線的變量或者方法無法直接訪問。好比:類 Foo 中聲明 __a, 則不能用 Foo.__a 的方式訪問,但能夠用 Foo._Foo__a 的方式訪問。`

8、程序入口

Python 屬於腳本語言,代碼的運行是經過解釋器對代碼文件進行逐行解釋執行來完成的。它不像其餘編程語言那樣有統一的入口程序,好比 Java 有 Main 方法,C/C++ 有 main 方法。Python 的代碼文件除了能夠被直接執行外,還能夠做爲模塊被其餘文件導入。全部的頂級代碼在模塊導入時都會被執行,當但願模塊被導入時,應該避免主程序被執行。這樣就須要把主程序放到 if __name__ == '__main__' 代碼塊中,好比:ide

def main():
      ...

if __name__ == '__main__':
    main()

一個包除了可以被導入外,也能夠經過 python -m package 的方式被直接執行,前提是包中須要有 __main__.py,這個文件能夠說是包的程序入口,包中有了這個文件就能夠用 Python 的 -m 參數來直接運行。

9、編碼建議

  • 一、儘量使用 'is' 和 'is not' 取代 '==',好比 if x is not None 要優於 if x != None,另外用 if x 效率更高。

Note: 等於比較運算符(==) 會調用左操做數的 __eq__ 函數,這個函數能夠被其任意定義,而 is 操做只是作 id 比較,並不會被自定義。同時也能夠發現 is 函數是要快於等於運算符的,由於不用查找和運行函數。

  • 二、用 "is not" 代替 "not ... is",前者的可讀性更好。
  • 三、使用基於類的異常,每一個模塊或包都有本身的異常類,此異常類繼承自 Exception。
  • 四、異常中儘可能不要使用裸露的 except,except 後應該跟具體的 exceptions。
  • 五、使用 startswith() 和 endswith() 代替切片進行序列前綴或後綴的檢查。
  • 六、使用 isinstance() 比較對象的類型,而不是 type(),好比:
# Yes:  
if isinstance(obj, int)

# No:  
if type(obj) is type(1)
  • 七、判斷序列是否爲空時,不用使用 len() 函數,序列爲空時其 bool 值爲 False,好比:
# Yes:  
if not seq
if seq

# No:  
if len(seq)
if not len(seq)
  • 八、字符串後面不要有大量拖尾空格。
  • 九、使用 join 合併的字符串,字符串方法 join 能夠合併 list、tuple、iterator 中的元素,效率比鏈接符 + 高。
  • 十、使用 while 1while True 更快。
  • 十一、使用 **pow 快 10 倍以上。
  • 十二、使用迭代器和生成器代替列表等數據結構效率更高,使用列表(字典)解析式和生成器表達式比用循環效率更高。
  • 1三、避免在循環中用 + 或 += 來連續拼接字符串。由於字符串是不變型,這會毫無必要地創建不少臨時對象,從而成爲二次方級別的運算量而不是線性運算時間。
  • 1四、多去了解標準庫,標準庫中用不少好用的功能,可以更優雅的解決問題,如 pkgutil.get_data()、operator.methodcaller() 等等。

參考資料

相關文章
相關標籤/搜索