2016年5月14日 星期六

Python 研究-程式碼風格指引

Python 程式碼風格指引
◎ 本文轉載自 http://wiki.jiayun.org/PEP_8_--_Style_Guide_for_Python_Code#References,原作者為 Guido van Rossum、Barry Warsaw,翻譯者為:JiaYun
簡介
本文件提供 Python 主要發行版本標準程式庫中的 Python 程式碼所用的撰寫慣例。關於 Python 的 C 實作中所用的 C 語言風格指引,請參考相關的 PEP[1]。
本文件改寫自 Guido 所寫的 Python 風格指引文章 [2],並增添一些 Barry 的風格指引 [5] 的內容。當兩者有衝突時,本 PEP 以 Guido 風格為準。本 PEP 可能仍未完成(事實上,可能永遠不會完工<眨眼>)。


愚蠢的一致性是小心眼中的妖怪
Guido 的重要見解之一是:程式碼被閱讀的次數,遠大於被撰寫的次數。提供本指引的目的,是為了增進程式碼的可讀性,並使 Python 程式碼在各方面保持一致性。如同 PEP 20 [6] 所說的「可讀性至關重要」。
風格指引關注的是一致性。和本指引一致很重要,在專案中保持一致性又更重要,但在 module 或 function 中保持一致性則最重要。
但更更重要的是:知道何時該不一致 -- 有時候風格指引就是無法適用。有疑惑時,運用你的最佳判斷力。看看其他例子,並決定何者最好看。需要的時候,儘管發問。
打破特定規則的兩個好理由:
1.    使用該規則會造成程式碼可讀性變差,特別是對習慣該規則的人來說,可讀性也變差的時候。
2.    為了和前後(可能是因為歷史性因素)打破該規則的程式碼保持一致性時 -- 雖然這也是清理他人雜亂程式碼的時機(在真正的 Extreme Programming 開發中)。


程式碼編排
縮排
每個縮排層級使用 4 個空白。
在相當舊的程式碼中,為了一致可以繼續使用 tab。
tab 或空白?
絕對不要混用 tab 和空白。
縮排 Python 最常用的方式是只用空白。第二常用的方式是只用 tab。混用 tab 和空白來縮排的程式碼,應該轉成只用空白。在呼叫 Python 直譯器時加上 -t 選項,它會對混用 tab 和空白的程式發出警告。若使用 -tt 選項,則發出的會是錯誤。非常推薦使用這些選項!
對於新專案來說,只用空白比用 tab 更受推薦。大多數編輯器都有相關設定,可以很容易做到這點。
每行最大長度
將每一行限制在最多 79 個字元之內。
仍然有很多裝置受限於每行 80 個字元;而且視窗寬度限制在 80 個字元內,可以方便讓多個視窗並排。這些裝置的預設斷行機制會破壞程式碼的顯示結構,而使程式碼更難理解。所以,請將每一行限制在最多 79 個字元之內。對於大區段的文字(docstring 或註解),建議每行限制在 72 個字元內。
建議的斷行方式是運用圓括號、方括號、大括號在 Python 中隱含的行接續作用。若需要,也可以對 expression 增加額外的圓括號,但有時只用反斜線看起來會更好。確保接續的行有妥善縮排。一些例子:
class Rectangle(Blob):

    def __init__(self, width, height,
                 color='black', emphasis=None, highlight=0):
        if width == 0 and height == 0 and \
           color == 'red' and emphasis == 'strong' or \
           highlight > 100:
            raise ValueError("sorry, you lose")
        if width == 0 and height == 0 and (color == 'red' or
                                           emphasis is None):
            raise ValueError("I don't think so")
        Blob.__init__(self, width, height,
                      color, emphasis, highlight)

空白行
將最高層級的 function 和 class 定義以兩個空白行分隔。
class 內的 method 定義之間以一個空白行分隔。
額外的空白行可以(謹慎地)用於分隔不同群組的相關 function。在一群相關的單行程式碼中(例如一組空的實作),空白行可以省略。
在 function 中,小心地使用空白行來表示邏輯上的分段。
Python 將 control-L(也就是 ^L)換頁字元視為空白。很多工具將這字元用於表示換頁,所以在你的檔案中,可以用它來為各相關區段作分頁。

編碼 (PEP 263)
Python 核心發行版本中的程式碼應該都使用 ASCII 或 Latin-1(即 ISO-8859-1)編碼。對於 Python 3.0 或以上版本,UTF-8 比 Latin-1 更建議使用,參考 PEP 3120。
使用 ASCII(或 UTF-8,在 Python 3.0 時)的程式檔案不該使用編碼指示(像是 "coding: utf-8")。Latin-1(或 UTF-8)應該只用在註解或 docstring 提到作者的名字需要時;否則建議在字串常量中,使用 \x、\u 或 \U 等轉義字符來表示非 ASCII 資料。
對於 Python 3.0 或以上版本,標準程式庫規定使用以下方針(參考 PEP 3131): 所有 Python 標準程式庫中的識別字「必須」使用僅含 ASCII 的識別字,而且「應該」在可能的時候都用英文的單字(很多情況下所用的縮寫或技術術語並不是英語)。另外,字串常量或註解也都必須是 ASCII 編碼。例外情況只能是 (a) 測試非 ASCII 功能的測試案例,和 (b) 作者的名字。名字不是拉丁字母組成的作者,「必須」提供名字的拉丁音譯。
對於擁有全球性使用者的開放原始碼專案,也鼓勵採用類似的方針。


import
    import 通常應該分成不同行,例如:
這樣寫:
import os
import sys
不要寫:
import sys, os
但這種情況是可以的:
from subprocess import Popen, PIPE
    import 應該永遠放在檔案頂端,也就是在 module 的註解和 docstring 之後,而在 module 的全域變數和常數之前。
    import 應該以以下順序分組:
1.    標準程式庫的 import
2.    相關第三方程式庫的 import
3.    己方應用程式/程式庫特定的 import
每組 import 之間應該以一個空白行分隔。 將任何相關的 __all__ 細述放在 import 之下。
    極不鼓勵在 package 之間使用相對 import。
所有 import 都要永遠用 package 的絕對路徑。雖然現在 PEP 328 [7] 已經在 Python 2.5 中完全實作,但它明確表明相對 import 的方式仍不鼓勵使用;絕對 import 更有可攜性,通常也更有可讀性。
    從含有 class 定義的 module 中 import class 時,這樣寫通常是可以的
from myclass import MyClass
from foo.bar.yourclass import YourClass
如果會造成命名衝突,可以改成
import myclass
import foo.bar.yourclass
然後使用 "myclass.MyClass" 和 "foo.bar.yourclass.YourClass"


expression 和 statement 中的空白
惱人瑣事
在以下情況避免使用額外的空白:
    緊連在圓括號、方括號、大括號之內。
這樣寫:
spam(ham[1], {eggs: 2})
不要寫:
spam( ham[ 1 ], { eggs: 2 } )
    逗號、分號、冒號前:
這樣寫:
    if x == 4: print x, y; x, y = y, x
不要寫:
    if x == 4 : print x , y ; x , y = y , x
    函式呼叫的參數傳遞左括號前:
這樣寫:
    spam(1)
不要寫:
spam (1)
    索引和 slice 的左括號前:
這樣寫:
    dict['key'] = list[index]
不要寫:
    dict ['key'] = list [index]
    在賦值(或其他)運算子前後用了一個以上的空白,只為了和另一個對齊。
這樣寫:
    x = 1
    y = 2
    long_variable = 3
不要寫:
    x             = 1
    y             = 2
    long_variable = 3

其他建議
    永遠在這些二元運算子前後加上一個空白:賦值(=)、增量賦值(+=, -= 之類)、比較(==, <, >, !=, <>, <=, >=, in, not in, is, is not)、邏輯(and, or, not)。
    算術運算子前後使用空白:
這樣寫:
i = i + 1
submitted += 1
x = x * 2 - 1
hypot2 = x * x + y * y
c = (a + b) * (a - b)
不要寫:
i=i+1
submitted +=1
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)
    當 '=' 符號是用在關鍵字參數或預設參數值時,不要加空白。
這樣寫:
    def complex(real, imag=0.0):
        return magic(r=real, i=imag)
不要寫:
    def complex(real, imag = 0.0):
        return magic(r = real, i = imag)
    複合 statement(一行有多個 statement)通常不鼓勵使用。
這樣寫:
    if foo == 'blah':
        do_blah_thing()
    do_one()
    do_two()
    do_three()
盡量不要寫:
    if foo == 'blah': do_blah_thing()
    do_one(); do_two(); do_three()
雖然有時 if/for/while 的主體短,可以整個放在同一行,但絕對不要在多子句時這麼做。也要避免摺疊這類長行!
盡量不要寫:
    if foo == 'blah': do_blah_thing()
    for x in lst: total += x
    while t < 10: t = delay()
更是別寫:
    if foo == 'blah': do_blah_thing()
    else: do_non_blah_thing()
   
    try: something()
    finally: cleanup()
   
    do_one(); do_two(); do_three(long, argument,
                                 list, like, this)
   
    if foo == 'blah': one(); two(); three()


註解
註解和程式碼牴觸比沒有註解還糟。更改程式碼之後,永遠將更新註解列為優先事項。
註解應該是完整的句子。當註解是片語或句子時,第一個單字應該大寫,除非它是一個小寫開頭的識別字(絕對不要改變識別字的大小寫!)。
果註解很短,結尾的句點可以省略。區塊註解通常包含由完整句子組成的一或多個段落,其中每個句子都該以句點作結尾。
每個句子的句點後應該加兩個空白。
寫英文時,Strunk 和 White 的 "The Elements of Style" 值得參考。
非英語系國家的 Python 程式設計師:請用英文寫註解,除非 120% 確定不會有不懂你語言的人閱讀你的程式碼。
區塊註解
區塊註解通常用來註解下方的一段(或全部)程式碼,並和程式碼使用相同縮排層級。區塊註解的每一行開頭都是 # 和一個空白(除非該行在註解中需要縮排)。
區塊註解中的段落之間,以只有一個 # 的行分隔。
行內註解
有節制地使用行內註解。
行內註解是和 statement 在同一行的註解。行內註解和 statement 之間應該至少用兩個空白分隔。行內註解的開頭應該是 # 和一個空白。
行內註解若只陳述明顯事實,則是不必要且實際上是造成干擾的。不要這樣寫:
x = x + 1                 # Increment x
但有時,這樣是有用的
x = x + 1                 # Compensate for border


文件字串
好的文件字串(即 docstring)撰寫慣例載於 PEP 257 [3]。
    所有 public 的 module、function、class、method 都該寫文件字串。非 public 的 method 不需要寫文件字串,但應該用註解描述該 method 的作用。這註解應該放在 "def" 行之下。
    PEP 257 敘述了好的文件字串慣例。注意最重要的是,多行文件字串結尾的 """ 應該自己獨立一行,而且前面最好加一個空白行,例如:
    """Return a foobang
   
    Optional plotz says to frobnicate the bizbaz first.
   
    """
    對於只有一行的 docstring,結尾的 """ 可以放在同一行。


版本紀錄
如果需要在原始檔中加進 Subversion、CVS 或 RCS 的資料,可以這樣做:
__version__ = "$Revision: 60919 $"
# $Source$
這幾行應該放在 module 的 docstring 之下,而在其他程式碼之前,並在上下各以一個空白行分隔。(譯註:Subversion 的話,第二行應該可以改用 "# $Id$")


命名慣例
Python 程式庫的命名慣例有點混亂,所以不太可能讓它完全一致 -- 不過這裡提供的是目前建議的命名標準。新的 module 和 package(包括第三方 framework)都應該依此標準撰寫,不過當現存的程式庫用的是不同風格時,保持內部的一致性比較重要。
命名風格敘述
有許多不同的命名風格,不論用在何處,能夠將它們辨認出來會很有幫助。
以下命名風格通常可以分辨出來:
    b (單個小寫字母)
    B (單個大寫字母)
    lowercase (小寫)
    lower_case_with_underscores (含底線的小寫)
    UPPERCASE (大寫)
    UPPER_CASE_WITH_UNDERSCORES (含底線的大寫)
    CapitalizedWords (字首大寫,又稱 CapWords 或 CamelCase -- 如此稱呼是因為字母看起來崎嶇不平[4]),有時也稱為 StudlyCaps。

注意:如果有縮寫字,將縮寫字的每個字母大寫。也就是寫 HTTPServerError 比 HttpServerError 好。
    mixedCase (類似字首大寫,只差開頭單字的第一個字母是小寫)
    Capitalized_Words_With_Underscores (字首大寫加底線,很醜!)
也有一種風格是用一個獨特的短字首將相關的名稱歸類在一起。這方法在 Python 中用得不多,但為了完整還是需要一提。例如 os.stat() function 回傳一個 tuple,其中各項的名稱習慣上都類似 st_mode,st_size,st_mtime 之類。(這是為了強調這些欄位和 POSIX system call struct 的對應關係,幫助程式設計師熟悉。)
X11 程式庫的所有 public function 都以 X 開頭。在 Python 中,這種風格通常是不需要的,因為 attribute 和 method 名稱前面都會接物件名稱,而 function 名稱前面則會接 module 名稱。
另外,以下是幾種開頭或結尾加底線的特殊格式(這些通常可以和任何大小寫慣例合用):
    _single_leading_underscore 開頭單一底線:不具強制力的「內部用」標示。在 "from M import *" 時不會 import 名稱是單一底線開頭的項目。
    single_trailing_underscore_ 結尾單一底線:慣例上用於避免和 Python 關鍵字衝突,例
    Tkinter.Toplevel(master, class_='ClassName')
    __double_leading_underscore 開頭雙底線:用來命名 class 的 attribute 時,會造成 name mangling(class FooBar 中,__boo 會變成 _FooBar__boo;參考下方相關處)。
    __double_leading_and_trailing_underscore__ 開頭和結尾雙底線:存在於使用者控制的命名空間中的「魔術」物件。例如 __init__、__import__、__file__。不要自創這類的名稱,只照文件說明去使用它們就好。

命名慣例規範
避免使用的名稱
不要用單個 `l'(小寫 L)、`O'(大寫 o)、`I'(大寫 i)當變數名稱。
在某些字型,這些字元和數字的一和零難以分辨。需要暫時用 `l' 時,改用 `L'。
package 和 module 名稱
module 應該用全小寫的簡短名稱。若能增加可讀性,可以在其中加入底線。Python 的 package 也該用全小寫的簡短名稱,但底線不鼓勵使用。
因為 module 名稱對應到檔案名稱,而有些檔案系統不會區分大小寫且會將長名稱縮短,所以選擇相當簡短的 module 名稱很重要 -- 雖然這在 Unix 上不是問題,但會在需要將程式移植到舊的 Mac 或 Windows 版本以及 DOS 時產生問題。
當 C 或 C++ 寫的擴充 module 有對應的 Python module 提供高階介面(比較物件導向)時,C/C++ 的 module 開頭會有一個底線(像是 _socket)。
class 名稱
幾乎沒有例外,class 名稱使用字首大寫慣例。僅供內部用的 class,名稱前會加一個底線。
exception 名稱
因為 exception 必須是 class,class 的命名慣例在此適用。不過 exception 實際是代表錯誤時,名稱必須用 "Error" 當字尾。
全域變數名稱
(希望這些變數只是為了在一個 module 內部使用。)全域變數命名慣例幾乎和 function 一樣。
設計以 "from M import *" 使用的 module 應該用 __all__ 機制避免將全域變數匯出,或者按照較舊的慣例將這些全域變數字首都加一個底線(你可能也想藉此來將這些全域變數標示為「module 內部用」)。
function 名稱
function 名稱應該全部小寫,為了可讀性,單字間可用底線分隔。
mixedCase 只能用在這種風格已佔多數的程式碼中(例如 threading.py),或者為了保持相容性時。
function 和 method 參數
instance method 的第一個參數永遠用 'self'。
class method 的第一個參數永遠用 'cls'。
當 function 參數名稱和保留的關鍵字衝突時,在字尾加一個底線比用縮寫或拼寫省略好。因此 "print_" 比 "prnt" 好。(可能更好的是用同義字避免這類衝突。)
method 名稱和 instance 變數
使用 function 的命名規則:小寫並在需要時用底線分隔單字增加可讀性。
只有非 public 的 method 和 instance 變數才在開頭加一個底線。
為了避免和 subclass 衝突,可在開頭加雙底線以利用 Python 的 name mangling 機制。
Python 會用 class 名稱 mangle 這些雙底線開頭的名稱:如果 class Foo 有個叫做 __a 的 attribute,它無法以 Foo.__a 存取。(執意存取的使用者可以透過 Foo._Foo__a 存取。)一般而言,雙底線開頭只應該用在避免為繼承設計的 class 的 attribute 名稱衝突。
注意:雙底線開頭的名稱有一些爭議(見下方)。

為繼承而設計
永遠決定好 class 的 method 和 instance 變數(全部統稱為:attribute)是該 public 或非 public。不確定時,選擇非 public;在之後將它改成 public 比將 public attribute 改成非 public 容易。
public attribute 是在保證不會有不相容變動下,預計供 class 的不相關用戶使用的。非 public attribute 則是不打算供第三方使用;不需要保證非 public 的 attribute 不會變動或甚至移除。
這裡不用 "private" 這個詞,是因為 Python 中沒有任何 attribute 可以真正是 private(若不費一番不必要的苦工)。
另一類 attribute 是那些屬於 "subclass API" 的一部分的(在其他語言通常叫做 "protected")。有些 class 是專門設計成要被繼承,以擴展或修改 class 的功能。設計這種 class 時,注意明確地決定哪些 attribute 是 public,哪些是 subclass API 的一部分,哪些則是只供 class 本身使用。
謹記這些 Pythonic 指導原則:
    public 的 attribute 開頭不該有底線。
    如果 public 的 attribute 名稱和保留的關鍵字衝突,在名稱後加一個底線。這樣做比用縮寫或省略拼寫好。(不過雖然有這項規則,'cls' 還是建議用在變數或參數是一個 class 的時候,特別是 class method 的第一個參數。)
注意:參考上面有關 class method 參數名稱的建議。
    對於簡單的資料型 public attribute,最好只提供 attribute 名稱,不要有複雜的 accessor/mutator method。要記得 Python 為以後的改進提供容易的辦法,以便你發現簡單的資料型 attribute 需要發展成函式型的功能時利用。在那種情況下,用 property 將函式性的實作隱藏在簡單資料型 attribute 的存取語法後面。
    注意一:property 只在 new-style class 中有作用。(譯註:new-style class 表示至少繼承了一個 class;classic class 則完全沒繼承別的 class)
    注意二:試著讓函式型功能沒有副作用,雖然像暫存之類的副作用通常是好的。
    注意三:避免將 property 用在需要大量運算的處理上;attribute 表示法會讓呼叫端相信它存取起來是(較)不費工的。
    如果 class 打算被繼承,但有些 attribute 不想讓 subclass 使用,考慮將它們命名成開頭有雙底線而結尾沒有底線。這會啟動 Python 的 name mangling 機制,讓 class 名稱被 mangle 到 attribute 的名稱。如此有助於避免 subclass 的 attribute 不小心用了相同名稱時造成的衝突。
    注意一:只有單單 class 名稱(不含 module 和 package)會用於 mangle 之後的名稱,所以如果 subclass 選擇了相同的 class 名稱和 attribute 名稱,還是會導致名稱衝突。
    注意二:name mangling 可能會使得某些用途上較不方便(像是 debug 和 __getattr__())。不過 name mangling 機制有完善的文件說明,很容易可以手動處理。
    注意三:不是每個人都喜歡 name mangling。試著在避免意外名稱衝突的需要,和使用者可能是進階使用者之間取得平衡。


程式撰寫建議
    程式應該以不會在特定 Python 實作(PyPy, Jython, IronPython, Pyrex, Psyco 之類)上不利的方式撰寫。
舉例來說,不要依賴 CPython 對像 a+=b 或 a=a+b 這類形式的 statement 的高效能就地字串串接實作。這些 statement 在 Jython 中執行比較慢。在程式庫效能要求高的部分,應該使用 ''.join() 形式。這樣可以確保串接在各種實作中,都只耗費線性時間。
    和像 None 這樣的 singleton 比較時,應該永遠使用 'is' 或 'is not',絕對不要用相等運算子。
也要注意寫了 "if x" 但其實是想表達 "if x is not None" 的情況 -- 例如,測試預設值是 None 的變數或參數是否設了新值時。新值可能是在邏輯判斷中有機會被當作 false 的型別(例如容器)!
    使用 class 型 exception
字串型 exception 在新程式碼中禁止使用,因為這功能將在 Python 2.6 移除。 module 和 package 應該定義它們自己特定的基礎 exception class,這些 class 應該是內建 Exception class 的 subclass。永遠寫上 class 的文件字串。例如:
class MessageError(Exception):
    """Base class for errors in the email package."""
class 命名慣例適用於此,只是如果 exception 是代表錯誤時,exception class 名稱字尾要加上 'Error'。非錯誤類的 exception 不需要加特定字尾。
    發出 exception 時,寫成 "raise ValueError('message')" 而不要用舊格式 "raise ValueError, 'message'"。
推薦括號格式是因為,exception 參數很長或包含格式化字串時,由於有括號,可以不必用行接續字元。舊的格式將在 Python 3000 (譯註:3.0)中移除。
    捕捉 exception 時,盡可能註明特定的 exception 而不要只用空的 'except:' 子句。
例如:
try:
    import platform_specific_module
except ImportError:
    platform_specific_module = None
空的 'except:' 子句會捕捉 SystemExit 和 KeyboardInterrupt 這兩個 exception,造成 Control-C 無法中止程式,而且可能掩蓋住其他問題。如果想捕捉所有代表程式錯誤的 exception,可以用 'except Exception:'。
有個好的法則是限制空的 'except' 子句只能用在兩種情況:
1.    exception 會被印出或記錄,至少使用者能注意到有錯誤發生。
2.    程式需要做些清理工作,但之後就用 'raise' 將 exception 往上發。'try ...finally' 是處理此情況較好的方式。
    此外,對於所有 try/except 子句,將 'try' 子句的程式碼數量限制在所需的最少數量。如此也能避免掩飾住 bug。
這麼寫:
try:
    value = collection[key]
except KeyError:
    return key_not_found(key)
else:
    return handle_value(value)
不要寫:
try:
    # Too broad! (太廣!)
    return handle_value(collection[key])
except KeyError:
    # Will also catch KeyError raised by handle_value()
    # (也會捕捉到 handle_value() 發出的 KeyError)
    return key_not_found(key)
    用字串的 method 而不要用 string module 的 function。
字串的 method 總是快得多,而且和 unicode 字串共用同樣的 API。需要相容於比 Python 2.0 舊的版本時,可以打破此規則。
    用 ''.startswith() 和 ''.endswith() 比對字串的字首和字尾,而不要用字串 slice。
startswith() 和 endswith() 更清楚明白且不容易出錯。例如:
這麼寫:
if foo.startswith('bar'):
不要寫:
if foo[:3] == 'bar':
唯一例外是你的程式碼必須在 Python 1.5.2 運行(希望不要)。
    比較物件型別應該永遠用 isinstance() 而不要直接比較型別。
這麼寫:
if isinstance(obj, int):
不要寫:
if type(obj) is type(1):
檢查物件是不是字串的時候,別忘了它也可能是 unicode 字串!Python 2.3 中,str 和 unicode 有共同的 base class -- basestring,所以可以這樣寫:
if isinstance(obj, basestring):
Python 2.2 中,types module 裡為此用途定義了 StringTypes 型別:
from types import StringTypes
if isinstance(obj, StringTypes):
Python 2.0 和 2.1 中,應該這麼寫:
from types import StringType, UnicodeType
if isinstance(obj, StringType) or \
   isinstance(obj, UnicodeType) :
    對於序列(string, list, tuple),利用空序列代表 false 的特性
這麼寫:
if not seq:
if seq:
不要寫:
if len(seq)
if not len(seq)
    不要寫出尾端需要有大量空白的字串常量。這些尾端的空白視覺上不易辨別,而且有些編輯器(或者像是 reindent.py)會將它們刪除。
    不要將邏輯值用 == 去和 True 或 False 比較
這麼寫:
if greeting:
不要寫:
if greeting == True:
更不要:
if greeting is True:


References
[1] PEP 7, Style Guide for C Code, van Rossum
[2] http://www.python.org/doc/essays/styleguide.html
[3] PEP 257, Docstring Conventions, Goodger, van Rossum
[4] http://www.wikipedia.com/wiki/CamelCase
[5] Barry's GNU Mailman style guide http://barry.warsaw.us/software/STYLEGUIDE.txt
[6] PEP 20, The Zen of Python
[7] PEP 328, Imports: Multi-Line and Absolute/Relative

沒有留言:

張貼留言