程式碼與文件風格指南 - 遺漏的細節#
這份文件收集了 SciPy 的程式碼與文件撰寫指南,這些指南並未明確地在現有的指南和標準中說明,包含:
其中有些看似瑣碎,可能不值得討論,但在許多情況下,這些問題已在 SciPy 或 NumPy 儲存庫的 pull request 審查中出現。如果一個風格問題重要到審查者會要求在合併前進行修改,那麼它就值得記錄下來——至少對於那些可以透過簡單規則解決的問題而言。
程式碼風格與指南#
請注意,儘管 docstring 是 Unicode,但通常應由 ASCII 字元組成。以下來自 tools/check_unicode.py
檔案的程式碼區塊告訴 linter 哪些額外的字元是允許的
18latin1_letters = set(chr(cp) for cp in range(192, 256))
19greek_letters = set('αβγδεζηθικλμνξoπρστυϕχψω' + 'ΓΔΘΛΞΠΣϒΦΨΩ')
20box_drawing_chars = set(chr(cp) for cp in range(0x2500, 0x2580))
21extra_symbols = set('®ő∫≠≥≤±∞²³·→√')
22allowed = latin1_letters | greek_letters | box_drawing_chars | extra_symbols
必要的關鍵字名稱#
對於參數多於幾個的新函式或方法,在最初幾個「顯而易見」的參數之後的所有參數,都應要求在使用時使用關鍵字。這透過在簽名中的適當位置包含 *
來實現。
例如,一個作用於單一陣列但具有多個可選參數(例如 method
、flag
、rtol
和 atol
)的函式 foo
將定義為
def foo(x, *, method='basic', flag=False, rtol=1.5e-8, atol=1-12):
...
要呼叫 foo
,除了 x
以外的所有參數都必須使用明確的關鍵字給定,例如 foo(arr, rtol=1e-12, method='better')
。
這強制呼叫者給出明確的關鍵字參數(即使不使用 *
,大多數使用者可能也會這樣做),而且這表示可以在 *
之後的任何位置向函式添加額外參數;新的參數不必添加到現有參數之後。
傳回物件#
對於傳回兩個或多個概念上不同的元素的新函式或方法,請以不可迭代的物件類型傳回這些元素。特別是,不要傳回 tuple
、namedtuple
,或由 scipy._lib._bunch.make_tuple_bunch
產生的「bunch」,後者保留用於向現有函式傳回的可迭代物件添加新屬性。相反地,使用現有的傳回類別(例如 OptimizeResult
)或新的自訂傳回類別。
這種傳回不可迭代物件的做法,迫使呼叫者更明確地說明他們想要存取的傳回物件的元素,並且使其更容易以向後相容的方式擴展函式或方法。
如果傳回類別很簡單且不是公開的(即無法從公共模組匯入),則可以像這樣記錄:
Returns
-------
res : MyResultObject
An object with attributes:
attribute1 : ndarray
Customized description of attribute 1.
attribute2 : ndarray
Customized description of attribute 2.
上面的「MyResultObject」沒有連結到外部文件,因為它足夠簡單,可以直接在其名稱下方完整記錄所有屬性。
有些傳回類別足夠複雜,值得擁有自己的渲染文件。如果傳回類別是公開的,這相當標準,但傳回類別只有在 1) 預期被終端使用者匯入且 2) 已獲得論壇批准時才應公開。對於複雜的私有傳回類別,請參閱 binomtest
如何總結 BinomTestResult
並連結到其文件,並注意 BinomTestResult
無法從 stats
匯入。
根據「MyResultObject」的複雜性,可以使用普通類別或 dataclass。當使用 dataclass 時,不要使用 dataclasses.make_dataclass
,而是使用正確的宣告。這允許自動完成列出結果物件的所有屬性,並改進靜態分析。最後,隱藏任何私有屬性
@dataclass
class MyResultObject:
statistic: np.ndarray
pvalue: np.ndarray
confidence_interval: ConfidenceInterval
_rho: np.ndarray = field(repr=False)
來自 numpy.testing
的測試函式#
在新程式碼中,不要使用 assert_almost_equal、assert_approx_equal 或 assert_array_almost_equal。以下是這些函式的 docstring:
It is recommended to use one of `assert_allclose`,
`assert_array_almost_equal_nulp` or `assert_array_max_ulp`
instead of this function for more consistent floating point
comparisons.
有關編寫單元測試的更多資訊,請參閱 NumPy 測試指南。
測試預期的例外狀況/警告#
當編寫新的測試,測試函式呼叫是否引發例外狀況或發出警告時,首選的風格是使用 pytest.raises
/pytest.warns
作為上下文管理器,並將應該引發例外狀況的程式碼放在上下文管理器定義的程式碼區塊中。match
關鍵字參數會附帶足夠的預期訊息,以將其與同一類別的其他例外狀況/警告區分開來。不要使用 np.testing.assert_raises
或 np.testing.assert_warns
,因為它們不支援 match
參數。
例如,函式 scipy.stats.zmap
應該在輸入包含 nan
且 nan_policy
為 "raise"
時引發 ValueError
。對此的測試是
scores = np.array([1, 2, 3])
compare = np.array([-8, -3, 2, 7, 12, np.nan])
with pytest.raises(ValueError, match='input contains nan'):
stats.zmap(scores, compare, nan_policy='raise')
match
參數確保測試不會因為引發與輸入包含 nan
無關的 ValueError
而通過。