代码和文档风格指南 - 遗漏的部分#
这是一份关于 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('αβγδεζηθικλμνξπρστυϕχψω' + 'ΓΔΘΛΞΠΣϒΦΨΩ')
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” 的复杂度,可以使用普通的类(class)或数据类(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。以下内容摘自这些函数的文档字符串
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 参数。
例如,如果输入包含 nan 且 nan_policy 为 "raise",函数 scipy.stats.zmap 应该引发 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 而通过。