代码和文档风格指南 - 缺失的部分#

这是 SciPy 的代码和文档指南的集合,这些指南并未在现有的指南和标准中明确说明,包括

其中一些是很琐碎的,可能不值得讨论,但在许多情况下,这个问题已经在 SciPy 或 NumPy 仓库的拉取请求审查中出现过。如果一个风格问题足够重要,以至于审阅者要求在合并之前进行更改,那么它就足够重要到需要记录下来——至少对于可以使用简单规则解决的问题来说是这样。

代码风格和指南#

请注意,尽管文档字符串是 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

必需的关键字名称#

对于具有多个参数的新函数或方法,在最初几个“明显”的参数之后的所有参数,在给定参数时都必须使用关键字。这是通过在签名中的适当位置包含 * 来实现的。

例如,一个操作单个数组但具有多个可选参数(例如 methodflagrtolatol)的函数 foo 将定义为

def foo(x, *, method='basic', flag=False, rtol=1.5e-8, atol=1-12):
    ...

要调用 foo,除了 x 之外的所有参数都必须使用显式关键字给出,例如 foo(arr, rtol=1e-12, method='better')

这强制调用者给出显式关键字参数(即使不使用 *,大多数用户可能也会这样做),并且这意味着可以在 * 之后函数的任何位置添加额外的参数;新参数不必添加到现有参数之后。

返回对象#

对于返回两个或多个概念上不同元素的新函数或方法,返回不可迭代的对象类型的元素。特别是,不要返回 tuplenamedtuple 或由 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” 的复杂性,可以使用普通类或数据类。使用数据类时,请勿使用 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_equalassert_approx_equalassert_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_raisesnp.testing.assert_warns,因为它们不支持 match 参数。

例如,如果输入包含 nannan_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 而通过。