文档字符串风格指南

一般指南

要为 SymPy 的文档字符串做出贡献,请完整阅读这些指南。

文档字符串(docstring)是一个字符串字面量,它作为模块、函数、类或方法定义中的第一个语句出现。这样的文档字符串成为该对象的 __doc__ 特殊属性。

示例

这是一个基本的文档字符串:

def fdiff(self, argindex=1):
    """
    Returns the first derivative of a Heaviside Function.

    Examples
    ========

    >>> from sympy import Heaviside, diff
    >>> from sympy.abc import x

    >>> Heaviside(x).fdiff()
    DiracDelta(x)

    >>> Heaviside(x**2 - 1).fdiff()
    DiracDelta(x**2 - 1)

    >>> diff(Heaviside(x)).fdiff()
    DiracDelta(x, 1)

    """

每个公共函数、类、方法和模块都应该有一个描述其功能的文档字符串。模块中特定于函数或类的文档应在该函数或类的文档字符串中。模块级别的文档字符串应讨论模块的目的和范围,并提供如何使用模块中函数或类的高层次示例。模块文档字符串是文件顶部的文档字符串,例如,solvers.ode 的文档字符串。

公共函数是指那些旨在供最终用户或公众使用的函数。公共函数的文档非常重要,因为它们会被许多人查看和使用。

另一方面,私有函数是指仅在 SymPy 本身的代码中使用的函数。虽然记录私有函数的重要性较低,但在私有函数上添加文档字符串也有助于其他 SymPy 开发者理解如何使用该函数。

并非总是清楚什么是公共函数,什么是私有函数。如果一个函数以一个下划线开头,那么它是私有的;如果一个函数包含在 __init__.py 中,那么它是公共的,但反之则不一定成立,因此有时你必须根据上下文来决定。一般来说,如果你不确定,有函数文档总比没有文档好,无论它是公共的还是私有的。

文档字符串应包含面向函数用户的信息。特定于代码的注释或其他可能只会分散用户注意力的笔记应放在代码中的注释中,而不是文档字符串中。

每个文档字符串都应该有展示函数如何工作的示例。示例是文档字符串中最重要的部分。一个展示输入和输出到函数的单个示例可能比一段描述性文本更有帮助。

记住,文档字符串的主要受众是其他人,而不是机器,因此用简单的英语描述函数的功能非常重要。同样,使用函数的示例应为人类读者设计,而不仅仅是为doctest机制设计。

请记住,虽然 Sphinx 是用户消费文档字符串的主要方式,因此在编写文档字符串(尤其是公共函数)时首先要考虑的平台,但它并不是用户消费文档字符串的唯一方式。你也可以使用 help() 或 IPython 中的 ? 来查看文档字符串。例如,使用 help() 时,它会显示所有私有方法的文档字符串。此外,任何直接阅读源代码的人都会看到每一个文档字符串。

所有公共函数、类、方法及其对应的文档字符串都应导入到 Sphinx 文档中,相关说明可在本指南末尾找到。

格式化

文档字符串是用 reStructuredText 格式编写的,该格式由 Sphinx 扩展。这里有一个简明的 Quick reStructuredText 指南。关于使用 reStructuredText 的更深入信息可以在 Sphinx 文档 中找到。

为了让 Sphinx 在 HTML 文档中很好地渲染文档字符串,编写文档字符串时应遵循一些格式指南:

  • 始终在文档字符串周围使用 """三重双引号"""。如果在文档字符串中使用任何反斜杠,请使用 r"""原始三重双引号"""

  • 在文档字符串的结束引号前包含一个空行。

  • 行不应超过 80 个字符。

  • 始终在类定义行下编写类级别的文档字符串,因为在源代码中这样阅读效果更好。

  • 类中的各种方法如果重要,可以在文档字符串或示例中提及,但它们的详细信息应放在方法本身的文档字符串中。

  • 请注意,:: 创建代码块,这在文档字符串中很少使用。任何带有示例 Python 的代码示例都应放在 doctest 中。始终检查由 Sphinx 渲染的最终版本在 HTML 中是否正确显示。

  • 为了使文档字符串中的节下划线工作得更好,使用了 numpydoc Sphinx 扩展

  • 始终仔细检查您的文档字符串是否格式正确:

  1. 确保你的文档字符串被导入到Sphinx中。

  2. 构建 Sphinx 文档 (cd doc; make html)。

  3. 确保Sphinx不输出任何错误。

  4. _build/html 中打开页面,并确保其格式正确。

章节

在 SymPy 的文档字符串中,建议函数、类和方法的文档字符串按以下顺序包含以下部分:

  1. 单句总结

  2. 解释

  3. 示例

  4. 参数

  5. 另请参阅

  6. 参考文献

单句摘要和示例部分对每个文档字符串是**必需**的。如果这些部分未包含在内,文档字符串将无法通过审查。

不要更改这些支持部分的名称,例如,即使只有一个示例,也应使用复数形式的标题“示例”。

SymPy 将继续支持 NumPy Docstring 指南 中列出的所有部分标题。

标题应该用等号进行相同长度的下划线。

如果不需要某个部分,并且该部分对于所讨论的函数的信息是不必要的,请不要使用它。不必要的部分和杂乱的文档字符串会使函数更难理解。目标是提供理解函数所需的最少信息量。

1. 单句总结

本节对于每个文档字符串是**必需**的。如果未包含此节,文档字符串将无法通过审查。本节不需要标题。

本节由一个以句号结尾的单行句子组成,描述了函数、类或方法的效果。

弃用警告应直接放在单句摘要之后,以便立即通知用户。弃用警告应以 deprecated 的形式写在 Sphinx 指令中:

.. deprecated:: 1.1

   The ``simplify_this`` function is deprecated. Use :func:`simplify`
   instead. See its documentation for more information.

更多详情请参见 弃用文档

2. 解释部分

本节是推荐的。如果你选择在你的文档字符串中包含一个解释部分,它应该用与等号相同长度的下划线标有“解释”标题。

Explanation
===========

本节包含对函数、类或方法功能的更详细描述,当简洁的单句摘要不足以说明时。本节应用于通过几句话或段落来澄清功能。

3. 示例部分

本节对于每个文档字符串是**必需的**。如果未包含,文档字符串将无法通过审查。它应以“示例”为标题(即使只有一个示例),并用等号下划线标注相同长度。

Examples
========

本节包含展示函数如何工作的示例,称为doctests。Doctests应足够复杂,以全面展示函数的API和功能,但又应足够简单,使用户无需过多思考即可理解。完美的doctest会告诉用户关于函数的所有他们需要知道的信息,而无需阅读docstring的其他部分。

在 doctest 之前应始终有一个空行。当提供多个示例时,它们应该用空行分隔。解释示例的注释应在上方和下方都有空行。

不要把doctests看作是测试。把它们看作是碰巧被测试的例子。它们应该向用户展示函数的API(即,输入参数是什么样的,输出是什么样的,以及它做了什么)。如果你只想测试某样东西,请在相关的``test_*.py``文件中添加一个测试。

你可以使用 ./bin/coverage_doctest.py 脚本来测试文件或模块的 doctest 覆盖率。使用 ./bin/doctest 运行 doctests。

只有在无法测试示例时,才应跳过其测试。如有必要,可以通过添加特殊注释来跳过示例的测试。

示例

>>> import random
>>> random.random()      
0.6868680200532414

如果一个示例超过80个字符,它应该被换行。示例应该被换行,以便它们仍然是有效的Python代码,使用``…``作为Python提示中的延续。例如,来自ODE模块文档:

示例

>>> from sympy import Function, dsolve, cos, sin
>>> from sympy.abc import x
>>> f = Function('f')
>>> dsolve(cos(f(x)) - (x*sin(f(x)) - f(x)**2)*f(x).diff(x),
... f(x), hint='1st_exact')
Eq(x*cos(f(x)) + f(x)**3/3, C1)

这里 dsolve(cos(f(x)) - (x*sin(f(x)) - f(x)**2)*f(x).diff(x), f(x), hint='1st_exact') 太长了,所以我们在一个逗号后换行,以便于阅读,并在续行上放置 ...。如果这样做不正确,doctests 将会失败。

命令的输出也可以是换行的。在这种情况下不应使用 ... 。doctester 自动接受换行后的输出。

示例

>>> list(range(30))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29]

在 doctest 中,像 sympy import ... 这样写导入,而不是 import sympyfrom sympy import *。要定义符号,使用 from sympy.abc import x,除非名称不在 sympy.abc 中(例如,如果它有假设),在这种情况下使用 symbols,如 x, y = symbols('x y')

通常,你应该运行 ./bin/doctest 以确保你的示例运行正确,并在它们不正确时进行修复。

4. 参数部分

本节是推荐的。如果你选择在你的文档字符串中包含一个参数部分,它应该用与等号相同长度的下划线标有“参数”标题。

Parameters
==========

如果在函数、类或方法名称后列出了括号中的参数,则必须包含一个参数部分。

本节包含函数参数、关键字及其相应类型的描述。

用双反引号包围变量。冒号前必须有一个空格,如果类型不存在则可以省略。对于参数类型,尽可能精确。如果不必要指定关键字参数,使用 optional 。可选关键字参数有默认值,这些值作为函数签名的一部分显示。它们也可以在描述中详细说明。

当一个参数只能取一组固定值中的一个时,这些值可以在大括号中列出,默认值排在第一位。当两个或多个输入参数具有完全相同的类型、形状和描述时,它们可以合并。

如果参数部分格式不正确,文档构建将呈现不正确。

如果你希望包含一个“返回”部分,请将其作为一个单独的部分,并使用自己的标题来编写。

示例

以下是正确格式化的参数部分的示例:

def opt_cse(exprs, order='canonical'):
    """
    Find optimization opportunities in Adds, Muls, Pows and negative
    coefficient Muls.

    Parameters
    ==========

    exprs : list of sympy expressions
        The expressions to optimize.
    order : string, 'none' or 'canonical'
        The order by which Mul and Add arguments are processed. For large
        expressions where speed is a concern, use the setting order='none'.

    """

5. 另请参阅部分

本节是推荐的。如果你选择在文档字符串中包含一个“另请参阅”部分,它应该以标题“另请参阅”标注,并用等号下划线标注相同长度。

See Also
========

本节包含相关函数、类和方法的列表。如果需要,相关项目可以用简洁的片段(不是完整的句子)来描述,但这不是必须的。如果描述超过一行,后续行必须缩进。

See Also 部分应仅用于引用其他 SymPy 对象。任何作为链接的内容都应作为超链接嵌入到文档字符串的文本中;详情请参见 References 部分。

不要使用 class:Classnameclass:`Classname`:class:`Classname` 来引用类,而应仅使用类名。

示例

以下是一个格式正确的“另请参阅”部分,带有简洁的描述:

class erf(Function):
    r"""
    The Gauss error function.

    See Also
    ========

    erfc: Complementary error function.
    erfi: Imaginary error function.
    erf2: Two-argument error function.
    erfinv: Inverse error function.
    erfcinv: Inverse Complementary error function.
    erf2inv: Inverse two-argument error function.

    """

以下是一个格式正确的“参见”部分,仅包含名称列表:

class besselj(BesselBase):
    r"""
    Bessel function of the first kind.

    See Also
    ========

    bessely, besseli, besselk

    """

6. 参考文献部分

本节是推荐的。如果你选择在文档字符串中包含一个参考文献部分,它应该用与等号长度相同的下划线标有“参考文献”标题。

References
==========

本节包含在前几节中引用的参考文献列表。任何对其他 SymPy 对象的引用应放在“另请参阅”部分。

参考资料部分应包括在线资源、论文引用和/或任何其他提供函数一般信息的印刷资源。参考资料旨在补充文档字符串,但不应要求理解它。参考资料按引用的顺序编号,从一开始。

对于在线资源,仅链接到可自由访问且稳定的在线资源,如维基百科、Wolfram MathWorld 和 NIST 数学函数数字图书馆(DLMF),这些资源不太可能遭受超链接失效的问题。

论文的参考文献应按以下顺序包含:引用标注、作者姓名、作品标题、期刊或出版物、出版年份、页码。

如果存在 DOI(数字对象标识符),请在引用中包含它,并确保它是一个可点击的超链接。

示例

以下是一个引用印刷资源的参考部分:

References
==========

.. [1] [Kozen89] D. Kozen, S. Landau, Polynomial Decomposition Algorithms,
       Journal of Symbolic Computation 7 (1989), pp. 445-456

以下是一个引用部分,引用了印刷和在线资源:

References
==========

.. [1] Abramowitz, Milton; Stegun, Irene A., "Chapter 9," Handbook of
       Mathematical Functions with Formulas, Graphs, and Mathematical
       Tables, eds. (1965)
.. [2] Luke, Y. L., The Special Functions and Their Approximations,
       Volume 1, (1969)
.. [3] https://en.wikipedia.org/wiki/Bessel_function
.. [4] https://functions.wolfram.com/Bessel-TypeFunctions/BesselJ/

示例文档字符串

以下是一个正确格式化的文档字符串示例:

class gamma(Function):
    r"""
    The gamma function

    .. math::
       \Gamma(x) := \int^{\infty}_{0} t^{x-1} e^{-t} \mathrm{d}t.

    Explanation
    ===========

    The ``gamma`` function implements the function which passes through the
    values of the factorial function (i.e., $\Gamma(n) = (n - 1)!$), when n
    is an integer. More generally, $\Gamma(z)$ is defined in the whole
    complex plane except at the negative integers where there are simple
    poles.

    Examples
    ========

    >>> from sympy import S, I, pi, oo, gamma
    >>> from sympy.abc import x

    Several special values are known:

    >>> gamma(1)
    1
    >>> gamma(4)
    6
    >>> gamma(S(3)/2)
    sqrt(pi)/2

    The ``gamma`` function obeys the mirror symmetry:

    >>> from sympy import conjugate
    >>> conjugate(gamma(x))
    gamma(conjugate(x))

    Differentiation with respect to $x$ is supported:

    >>> from sympy import diff
    >>> diff(gamma(x), x)
    gamma(x)*polygamma(0, x)

    Series expansion is also supported:

    >>> from sympy import series
    >>> series(gamma(x), x, 0, 3)
    1/x - EulerGamma + x*(EulerGamma**2/2 + pi**2/12) +
    x**2*(-EulerGamma*pi**2/12 - zeta(3)/3 - EulerGamma**3/6) + O(x**3)

    We can numerically evaluate the ``gamma`` function to arbitrary
    precision on the whole complex plane:

    >>> gamma(pi).evalf(40)
    2.288037795340032417959588909060233922890
    >>> gamma(1+I).evalf(20)
    0.49801566811835604271 - 0.15494982830181068512*I

    See Also
    ========

    lowergamma: Lower incomplete gamma function.
    uppergamma: Upper incomplete gamma function.
    polygamma: Polygamma function.
    loggamma: Log Gamma function.
    digamma: Digamma function.
    trigamma: Trigamma function.
    beta: Euler Beta function.

    References
    ==========

    .. [1] https://en.wikipedia.org/wiki/Gamma_function
    .. [2] https://dlmf.nist.gov/5
    .. [3] https://mathworld.wolfram.com/GammaFunction.html
    .. [4] https://functions.wolfram.com/GammaBetaErf/Gamma/

    """

数学函数的类文档字符串

SymPy 的一个不寻常之处在于它也有一些类是数学函数。对于那些作为数学函数的类,其文档字符串应包含针对此类类的特定细节,如下所述:

  • 解释部分应包括函数的数学定义。这应使用 LaTeX 数学。使用 $$ 表示 行内数学,使用 .. math:: 表示显示数学,这应用于主要定义。公式中的变量名称应与参数名称匹配,LaTeX 格式应与 SymPy 使用的 LaTeX 漂亮打印匹配。如有相关,数学定义应提及其定义域,特别是当定义域不同于复数时。

  • 如果在文献中对一个函数有多种约定,请确保清楚地指定 SymPy 使用哪种约定。

  • 解释部分可能还包括一些关于该函数的重要数学事实。这些可以在示例部分中交替展示。数学讨论不应太长,因为用户可以查看参考资料以获取更多详细信息。

  • 文档字符串不需要讨论每个实现细节,例如在函数的哪些操作上定义或在“eval”方法的哪些点上进行评估。这些重要的或具有启发性的实例可以在示例部分展示。

  • 文档字符串应该放在类级别(紧接在包含“class”的行下方)。“eval”方法不应该有文档字符串。

  • 类中的私有方法,即任何以单下划线开头的方法,不需要进行文档化。如果你愿意,它们仍然可以被文档化,但请注意,这些文档字符串不会被拉入Sphinx文档中,因此它们只能被阅读代码的开发者看到,所以如果有任何非常重要的事项需要提及,也应该在类级别的文档字符串中提及。

编写文档字符串的最佳实践

在编写文档字符串时,请遵循与编写叙述性文档时相同的格式、风格和语气偏好。有关指南,请参阅 编写文档的最佳实践,格式、风格和语气。

将文档字符串导入Sphinx文档

以下是从 doc/src/modules/geometry 目录中摘录的内容,这些内容将几何模块的相关文档字符串导入到文档中:

Utils
=====

.. module:: sympy.geometry.util

.. autofunction:: intersection

.. autofunction:: convex_hull

.. autofunction:: are_similar

Points
======

.. module:: sympy.geometry.point

.. autoclass:: Point
   :members:

Lines
=====

.. module:: sympy.geometry.line

.. autoclass:: LinearEntity
   :members:

.. autoclass:: Line
   :members:

.. autoclass:: Ray
   :members:

.. autoclass:: Segment
   :members:

Curves
======

.. module:: sympy.geometry.curve

.. autoclass:: Curve
   :members:

Ellipses
========

.. module:: sympy.geometry.ellipse

.. autoclass:: Ellipse
   :members:

.. autoclass:: Circle
   :members:

Polygons
========

.. module:: sympy.geometry.polygon

.. autoclass:: Polygon
  :members:

.. autoclass:: RegularPolygon
   :members:

.. autoclass:: Triangle
   :members:

首先,使用 .. module:: 指令将命名空间设置为特定的子模块(文件),然后使用 .. autoclass::.. autofunction:: 相对于该子模块(文件)导入文档字符串。其他方法要么使用起来繁琐(对所有对象使用完整路径),要么会破坏某些功能(使用 .. module:: sympy.geometry 相对于主模块导入会破坏 viewcode Sphinx 扩展)。doc/src/modules/ 中的所有文件都应使用此格式。

交叉引用

任何引用其他 SymPy 函数的文本都应格式化,以便自动创建指向该函数文档的交叉引用链接。这是通过使用 RST 交叉引用语法完成的。这里有两种不同类型的对象有约定:

1. Objects that are included in from sympy import *, for example, sympy.acos.

For these, use :obj:`~.acos()`. The ~ makes it so that the text in the rendered HTML only shows acos instead of the fully qualified name sympy.functions.elementary.trigonometric.acos. (This will encourage importing names from the global sympy namespace instead of a specific submodule.) The . makes it so that the function name is found automatically. (If Sphinx gives a warning that there are multiple names found, replace the . with the full name. For example, :obj:`~sympy.solvers.solvers.solve()`.) Adding a trailing pair of parentheses is a convention for indicating the name is a function, method, or class.

您也可以使用更具体的类型指示符来代替 obj (参见 https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#cross-referencing-python-objects)。然而,obj 总是有效的,有时 SymPy 的名称并不是您可能期望的类型。例如,数学函数对象如 sin 实际上不是 Python 函数,而是一个 Python 类,因此 :func:`~.sin` 将不起作用。

2. Objects that are not included in from sympy import *, for example, sympy.physics.vector.dynamicsymbols.

This can be public API objects from submodules that are not included in the main sympy/__init__.py, such as the physics submodule, or private API objects that are not necessarily intended to be used by end-users (but should still be documented). In this case, you must show the fully qualified name, so do not use the ~. syntax. For example, :obj:`sympy.physics.vector.dynamicsymbols()`.

You may also write custom text that links to the documentation for something using the following syntax :obj:`custom text<object>`. For example, :obj:`the sine function <.sin>` produces the text “the sine function” that links to the documentation for sin. Note that the ~ character should not be used here.

Note that references in the See Also section of the docstrings do not require the :obj: syntax.

如果生成的交叉引用书写不正确,Sphinx 在构建文档时会报错,例如:

WARNING: py:obj reference target not found: expand

以下是一些解决错误的故障排除提示:

  • 确保你使用了如上所述的正确语法。

  • 确保你正确拼写了函数名称。

  • 检查您尝试交叉引用的函数是否确实包含在Sphinx文档中。如果没有,Sphinx将无法为其创建引用。在这种情况下,您应按照 文档字符串指南 中的描述将其添加到适当的RST文件中。

  • 如果函数或对象未包含在 from sympy import * 中,您需要使用完全限定名称,例如 sympy.submodule.submodule.function 而不是仅使用 function

  • 一个完全限定的名称必须包括函数的完整子模块,一直到文件。例如,sympy.physics.vector.ReferenceFrame 将无法工作(即使你可以在代码中以这种方式访问它)。它必须是 sympy.physics.vector.frame.ReferenceFrame

  • If the thing you are referring to does not actually have somewhere to link to, do not use the :obj: syntax. Instead, mark it as code using double backticks. Examples of things that cannot be linked to are Python built in functions like int or NotImplementedError, functions from other modules outside of SymPy like matplotlib.plot, and variable or parameter names that are specific to the text at hand. In general, if the object cannot be accessed as sympy.something.something.object, it cannot be cross-referenced and you should not use the :obj: syntax.

  • If you are using one of the type specific identifiers like :func:, be sure that the type for it is correct. :func: only refers to Python functions. For classes, you need to use :class:, and for methods on a class you need to use :method:. In general, it is recommended to use :obj:, as this will work for any type of object.

  • 如果你无法让交叉引用语法正常工作,直接提交拉取请求并请求审阅者帮助。

您可能还会看到类似以下的错误:

WARNING: more than one target found for cross-reference 'subs()':
sympy.core.basic.Basic.subs, sympy.matrices.matrixbase.MatrixBase.subs,
sympy.physics.vector.vector.Vector.subs,
sympy.physics.vector.dyadic.Dyadic.subs

for instance, from using :obj:`~.subs`. This means that the . is not sufficient to find the function, because there are multiple names in SymPy named subs. In this case, you need to use the fully qualified name. You can still use ~ to make it shortened in the final text, like :obj:`~sympy.core.basic.Basic.subs`.

Python 文件中警告的行号是相对于文档字符串的顶部,而不是文件本身。这些行号通常不完全正确,因此您通常需要在文档字符串中搜索警告所指的部分。这是由于 Sphinx 中的一个错误。