譯註:所謂 docstring 就是放在程式碼中用以說明某物件功能的字串常數,通常可以被 IDE 或其他軟體工具取出並顯示給開發者查閱使用,雖然功能是用來說明程式碼功能的,但不同於一般塞在程式碼裡一行一行的註解 (comments),docstring 著重的是物件整體的概括說明。
摘要
這份 PEP 記載了關於 Python docstring 的語義內容與撰寫慣例。
合理性
這份 PEP 的目的是將 docstring 的 high-level structure 進行標準化:docstrings 應該包含什麼、還有怎麼表達內容(但不涉及任何 docstring 中的標記語法等內容)。 這份 PEP 包含的是慣例,而非強制要求或語法。
「一個普遍的慣例將支持著所有的可維護性、清晰度、一致性以及良好撰寫習慣的基礎。但他絕對不會達成的,就是要求你違背你自己的意思及作法。這就是 Python!」
- Tim Peters,2001-06-16
如果你不遵守這些慣例,你頂多就是得到一些白眼。但某些軟體(例如 Docstring [4] docutils 處理系統 [1] [2] )會識別這些慣例,所以您遵從這些慣例將使你得到最好的結果。
規格說明
Docstring 是什麼?
Docstring 是放在模組 (module)、函式 (function)、類別 (class)、或方法 (method) 定義中第一個敘述 (statements) 處的字串常數 (string literal;譯註:即單純的字串內容,如 'abc' 或 ''' a string ''')。這樣的 docstring 會成為該物件的 __doc__ 特殊屬性。
所有的模組都應該要有 docstrings,而且所有模組中可自外部調用的函數和類別也應該要有 docstrings。 所有公共方法(包含建構子 __init__)也都應該有 docstrings。 關於套件的相關說明則可能被記錄在該套件目錄下 __init__.py 檔中的 docstring 裡。
出現在 Python 程式碼中其他地方的字串常數也有可能被當作文件來使用。但這些字串將無法被 Python 編譯器識別,而且也無法以執行時期物件屬性的方式來存取(也就是不會被指派給 __doc__)。但有兩種額外的 docstrings 是可能可以被特殊工具所取出的:
- 字串常數字符串字面量在頂層的一個模塊,類或__init__方法一個簡單的賦值發生後,立即被稱為“屬性的文檔字符串”。
- 發生後,立即另一個文檔字符串的字符串文字被稱為“額外的文檔字符串”。
為了保持一致性,請總是使用 """三個雙引號""" 來建立 docstrings。 如果你在你的 docstring 中有使用任何的反斜線,則請使用 r"""代表原始字串的三個雙引號"""。對於Unicode docstrings,則使用 u"""代表 Unicode 字串的三個雙引號"""。
Docstrings 共有兩種形式:單行與多行 docstrings。
單行 docstrings
單行 docstring 僅應使用於說明內容真的很明顯,並且真的只需要寫成一行時。例如:
def kos_root():
""" Return the pathname of the KOS root directory. """
global_kos_root
if _kos_root: return _kos_root
...
注意:
- 即便字串可以完全放在一行中,也使用三個雙引號。這可以讓 docstring 之後能容易地擴展。
- 終止引號與起始引號是放在同一行的。這能讓單行 docstring 看起來更美觀。
- 不管在 docstring 之前或之後都沒有空白行。
- Docstring 的內容應該是以一段時間作為結束的短句子。它應該以類似命令的方式來規定函式或方法的效果(像是:"Do this"、"Return that"),而不是一段描述。例如,不要寫成 "Returns the pathname..."。
- 單行 docstring 不應該只是單單重複方法參數與回傳值的 "signature"(那些資訊可以通過 introspection 得到)。例如,不要像下面這樣:
- def function(a, b):
""" function(a, b) -> list """ - 這種 docstring 只適用於 C函數中(例如在製作 plug-ins 時),不可能使用 introspection 時。然而,由於在一般 Python 程式中即便使用 introspection 依舊無法得知函式或方法回傳值的型態,所以回傳值的意義與資料型態依舊應該在 docstring 中說明。因此我們偏好的 docstring 形式是這樣的:
def function (a, b):
""" Do X and return a list. """
(當然,X 應該要是一個有用的描述!)
多行 docstrings
多行 docstring 的第一個部份是一句像是單行 docstring 般用以說明整個物件功能摘要的語句,後面接著一行空白行,然後是一段更詳細的描述。為了讓摘要行(譯註:第一行)可以被自動索引工具所使用,請務必將它只寫成一行,並與該 docstring 的其他部份分開。摘要行,可能與起始引號位在同一行,或位於起始引號的下一行。整個 docstring 則應該與第一行的引號有著相同的縮排(見下面的例子)。
所有在類別中的 docstrings(包含單行與多行docstring)都應該在首行之前與尾行之後各插入一個空行。一般來說,類別的方法彼此間會各以一個空白行來分隔,而 docstring 又需要以一個空白行來與第一個方法做區分。因此,為了對稱的緣故,我們也會在類別的開頭處與 docstring 間加入一個空白行。函式或方法的 docstring 一般不會有這樣的要求,除非該函式或方法的主體被寫成了數個以空白行分離的段落-在這種情況下,我們也會把 docstring 當作是其中一個段落,並在它前面加上一個空白行。
Script(一支獨立的 Python 程式)的 docstring 應該要作為其「使用方法」,當該程式被以不正確的或缺漏的參數叫用時顯示在使用者面前(或透過 "-h" 選項,當作 "help" 來使用)。這種 docstring 應該要記錄該程式的功能和命令列語法、環境變數、與相關檔案等。使用方法訊息
可能相當的詳盡(長到能塞滿好幾個螢幕),而且資訊應該要充足到對於新的使用者來說能正確的使用,而且對舊使用者來說也要能成為一份關於選項與參數的詳盡參考。
模組的 docstring 則應該以單行摘要的方式,針對所能提供給外界使用的類別、例外、與函式(以及任何其他物件)列舉並說明(與物件本身的 docstring 相比,這些摘要行給的資訊一般來說較少。)套件的 docstring(亦即套件中 __ init__.py 模組的 docstring)也應該詳列並說明可供外界使用的各模組與子套件。
函式或方法的 docstring 應該總結自己的行為並記錄其參數、回傳值、副作用、可能發生的例外、以及被調用時的限制等(如果可以的話,請全部記載)。選擇性參數也應該標明。關鍵字參數是否為該調用界面的一部分也應該被記載下來。
類別的 docstring 應該總結自己的行為,並列出其公共方法及實體變數。如果該類別的用途就是要拿來被繼承的,而且對於子類別來說有額外的界面可使用時,該界面應該在 docstring 中另外說明。類別建構子行為應該被記錄其 __init__ 方法的 docstring 中。其他個別的方法行為則應該記錄在自己的 docstring 裡。
如果一個類別繼承自另一個類別,而且其行為主要來自那個類別的話,那個 docstring 則應該提到這一點,並說明兩類別行為的不同之處。請使用動詞「覆載」(override) 來說明一個子類別方法已取代了父類別版本的同名稱方法,而且不調用父類別版本的時候;或者使用「擴增」(extend) 說明子類別方法會調用父類別版本方法的情況。
不要使用 Emacs 在執行時期時一提到函式或方法的參數就會使用大寫的那種慣例。Python 是 case-sensitive 的,而且參數名稱是可以當作關鍵字參數使用的。所以 docstring 中應該記錄正確的參數名稱。最好對於每個參數都以獨立的一行列出並說明。例如:
def complex(real= 0.0, imag= 0.0):
""" 建立一個複數
Keyword arguments:
real - 實數部份 (default 0.0)
imag - 虛數部份 (default 0.0)
"""
if imag == 0.0 and real == 0.0: return complex_zero
...
BDFL [3] 建議在多行 docstring 的最後一個段落與終止引號間插入一個空白行,並讓終止引號自己成為一行。如此一來,Emacs 的 fill-paragraph 命令就可以使用了。
處理 docstring 縮排
Docstring 處理工具一般來說會將 doctring 中自第二行開始的所有內容裡的縮排進行處理,並把各行的縮排空白數刪除至各行的最小值。Docstring 中第一行(直到第一個 new line 出現為止)的縮排都是無關緊要的並且會被刪除掉。其餘各行的相對縮排則會被保留下來。Docstring 開頭與結束的空白行則應該被刪除掉。
由於程式碼比文字更精確,這裡是上述演算法的一個實作方式:
def trim(docstring)
if not docstring:
return ''
# 將tab轉為space(遵循正常的Python規範)
# 並將其切成字串list:
lines = docstring.expandtabs().splitlines()
# 決定最小縮排數(第一行不計):
indent = sys.maxint
for line in lines[1:]:
stripped = line.lstrip()
if stripped:
indent = min(indent, len(line) - len(stripped))
# 刪掉縮排(第一行應額外處理):
trimmed = [lines[0].strip()]
if indent < sys.maxint:
for line in lines[1:]:
trimmed.append(line[indent:].rstrip())
# 去掉頭尾的空白行
while trimmed and not trimmed[-1]:
trimmed.pop()
while trimmed and not trimmed[0]:
trimmed.pop(0)
# 回傳處理完畢的單一字串物件
return '\n'.join(trimmed)
這個例子共有兩個 new line 字元與三行文字,頭尾兩航都是空白的。
def foo():
"""
This is the second line of the docstring.
"""
將上述程式碼套用至此例中:
>>> print repr(foo.__doc__)
'\n This is the second line of the docstring.\n '
>>> foo.__doc__.splitlines()
['', ' This is the second line of the docstring.', ' ']
>>> trim(foo.__doc__)
'This is the second line of the docstring.'
在經過處理後,這些以下兩個 docstring 是等價的:
def foo():
"""A multi-line
docstring.
"""
def bar():
"""
A multi-line
docstring.
"""
參考和註腳
[1] PEP 256, Docstring Processing System Framework, Goodger (http://www.python.org/dev/peps/pep-0256/)
[2] (1, 2) PEP 258, Docutils Design Specification, Goodger (http://www.python.org/dev/peps/pep-0258/)
[3] Guido van Rossum, Python's creator and Benevolent Dictator For Life.
[4] http://docutils.sourceforge.net/
[5] http://www.python.org/doc/essays/styleguide.html
[6] http://www.python.org/sigs/doc-sig/
沒有留言:
張貼留言