Docstrings 样式指南

一般指南

要为 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 扩展。这是一份简洁的 快速 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 文档字符串指南 中列出的所有部分标题。

标题应使用相同长度的等号下划线。

如果某个部分不是必需的,并且该函数不需要这些信息,请不要使用它。不必要的部分和混乱的文档字符串会使函数更难理解。以最少的必要信息来理解函数为目标。

1. 单句摘要

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

此部分包含一个以句号结尾的单行句子,描述函数、类或方法的效果。

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

.. 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 会告诉用户有关函数的所有必要信息,而无需阅读文档字符串的任何其他部分。

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

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

可以使用 ./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
========

此部分包含相关函数、类和方法的列表。如果需要,可以对相关项进行简要描述(不是完整的句子),但这并不是必需的。如果描述跨越多行,则后续行必须缩进。

另请参阅部分应仅用于引用其他 SymPy 对象。任何链接都应该作为超链接嵌入到文档字符串的文本中;有关详细信息,请参阅参考文献部分。

不要使用 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. 包含在 from sympy import * 中的对象,例如 sympy.acos

对于这些对象,使用 :obj:`~.acos()`~ 使渲染后的 HTML 文本中只显示 acos,而不是完全限定名 sympy.functions.elementary.trigonometric.acos。(这将鼓励从全局 sympy 命名空间而不是特定的子模块导入名称。). 使得函数名可以自动找到。(如果 Sphinx 警告发现多个名称,请将 . 替换为完整名称。例如,:obj:`~sympy.solvers.solvers.solve()`。)添加一对尾随括号是表示名称是函数、方法或类的约定。

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

2. 不包含在 from sympy import * 中的对象,例如 sympy.physics.vector.dynamicsymbols

这可以是来自子模块的公共 API 对象,这些对象未包含在主 sympy/__init__.py 中,例如物理子模块,或者私有 API 对象,这些对象并非旨在供最终用户使用(但仍应记录)。在这种情况下,您必须显示完全限定名,因此不要使用 ~. 语法。例如,:obj:`sympy.physics.vector.dynamicsymbols()`

您也可以使用自定义文本,该文本链接到使用以下语法的对象的文档 :obj:`custom text<object>`。例如,:obj:`the sine function <.sin>` 生成文本“正弦函数”,该文本链接到 sin 的文档。请注意,这里不应该使用 ~ 字符。

请注意,文档字符串的 参见 部分中的引用不需要 :obj: 语法。

如果生成的交叉引用写错了,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

  • 如果您引用的内容实际上没有链接目标,请不要使用 :obj: 语法。相反,请使用双反引号将其标记为代码。无法链接到的内容示例包括 Python 内置函数,例如 intNotImplementedError,来自 SymPy 之外的其他模块的函数,例如 matplotlib.plot,以及特定于当前文本的变量或参数名称。一般来说,如果无法以 sympy.something.something.object 的方式访问该对象,则无法进行交叉引用,也不应使用 :obj: 语法。

  • 如果您使用的是 特定类型 的标识符,例如 :func:,请确保其类型正确。 :func: 仅指 Python 函数。对于类,您需要使用 :class:,而对于类上的方法,您需要使用 :method:。一般来说,建议使用 :obj:,因为它适用于任何类型的对象。

  • 如果您无法使用交叉引用语法,请继续提交拉取请求,并向审阅者寻求帮助。

您也可能会看到以下错误:

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

例如,使用 :obj:`~.subs` 产生的错误。这意味着 . 不足以找到该函数,因为 SymPy 中存在多个名为 subs 的名称。在这种情况下,您需要使用完全限定名称。您仍然可以使用 ~ 在最终文本中将其缩短,例如 :obj:`~sympy.core.basic.Basic.subs`

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