如何在 Python 中记录模块常量?

Posted

技术标签:

【中文标题】如何在 Python 中记录模块常量?【英文标题】:How to document a module constant in Python? 【发布时间】:2013-12-12 04:26:08 【问题描述】:

我有一个模块,errors.py,其中定义了几个全局常量(注意:我知道 Python 没有常量,但我已经按照约定使用大写来定义它们)。

"""Indicates some unknown error."""
API_ERROR = 1

"""Indicates that the request was bad in some way."""
BAD_REQUEST = 2

"""Indicates that the request is missing required parameters."""
MISSING_PARAMS = 3

使用 reStructuredText 如何记录这些常量?如您所见,我在它们上方列出了一个文档字符串,但我没有找到任何表明这样做的文档,我只是猜测。

【问题讨论】:

“我如何记录这些常量?”是什么意思。添加 cmets(或您的情况下的字符串)已经是一种文档形式。您是在问如何让 sphinx-autodoc 识别您的内部文档并将其显示在 sphinx 的输出中? 另见numpydoc: documenting constants 【参考方案1】:

不幸的是,变量(和常量)没有文档字符串。毕竟,该变量只是一个整数的名称,您不希望将文档字符串附加到数字 1 上,就像附加到函数或类对象一样。

如果您查看 stdlib 中的几乎所有模块,例如 pickle,您会发现它们使用的唯一文档是 cmets。是的,这意味着help(pickle) 只显示这个:

DATA
    APPEND = b'a'
    APPENDS = b'e'
    …

... 完全无视 cmets。如果您希望您的文档显示在内置帮助中,则必须将它们添加到模块的文档字符串中,这并不理想。


但 Sphinx 可以做的比内置帮助更多。您可以将其配置为提取常量上的 cmets,或使用 autodata 半自动完成。例如:

#: Indicates some unknown error.
API_ERROR = 1

在任何赋值语句之前有多个#: 行,或者在语句右侧的单个#: 注释,其工作方式与自动文档拾取的对象上的文档字符串相同。其中包括处理内联 rST,并为变量名自动生成 rST 标头;你不需要做任何额外的事情来完成这项工作。


作为旁注,您可能需要考虑使用enum,而不是像这样的单独常量。如果你没有使用 Python 3.4(你可能还没有……),有一个用于 3.2+ 的 backport.enum 包,或 flufl.enum(不完全相同,但相似,因为它是主要灵感对于 stdlib 模块)2.6+。

枚举实例(不是flufl.enum,而是stdlib/backport 版本)甚至可以有文档字符串:

class MyErrors(enum.Enum):
    """Indicates some unknown error."""
    API_ERROR = 1

    """Indicates that the request was bad in some way."""
    BAD_REQUEST = 2

    """Indicates that the request is missing required parameters."""
    MISSING_PARAMS = 3

虽然很遗憾它们没有出现在 help(MyErrors.MISSING_PARAMS) 中,但它们是 Sphinx autodoc 可以获取的文档字符串。

【讨论】:

“毕竟,变量只是一个整数的名字”——而函数名是一个变量,它只是一个函数的名字,一个类名是一个变量,它只是一个一个类的名称......内部的函数和类只是一些大整数...... @Alexey 我认为您错过了那句话的重点。函数名称没有有文档字符串——函数有。而且你不想给数字 1 一个文档字符串,上面写着API_ERROR 好吧,但是 OP 的问题是关于记录“常量”,其值可以是函数、类或任何东西。允许记录一种类型的值但禁止记录另一种类型的值有点人为的限制,但这并非完全不合理。但是,如果使用VAL = 语法定义任何值(包括函数和类),Python 似乎没有一种方便的机制来记录它们。考虑MyClass = namedtuple("MyClass", ("foo", "bar")) 我没有将MyClass.__doc__ = 算作一种“方便”的机制,因为它需要提及记录的对象并显式操作其__doc__ 属性,并且感觉与将文档放入cmets 完全不同,但也许我我很挑剔... namedtuple 只是一个例子。将函数和类构造为函数的返回值,并希望记录它们,原则上没有什么不合理的。【参考方案2】:

如果您在变量之后 放置一个字符串,那么 sphinx 会将其作为变量的文档。我知道它有效,因为我到处都这样做。像这样:

FOO = 1
"""
Constant signifying foo.

Blah blah blah...
"""  # pylint: disable=W0105

pylint 指令告诉 pylint 避免将文档标记为无效语句。

【讨论】:

你可以使用这个新的注解=> .. autodata:: FOO :annotation: ... 见sphinx-doc.org/ext/autodoc.html#directive-autoattribute 更新链接 (@macm) sphinx-doc.org/en/master/usage/extensions/… - 请参阅文档中描述的示例,该示例显示了文档字符串的所有 3 种可能变体。【参考方案3】:

这是一个较老的问题,但我注意到缺少相关答案。

或者您可以通过.. py:data:: 在模块的文档字符串中包含对常量的描述。这样,文档也可以通过交互式帮助获得。 Sphinx 会很好地渲染它。

"""
Docstring for my module.

.. data:: API_ERROR

    Indicates some unknown error.

.. data:: BAD_REQUEST

    Indicates that the request was bad in some way.

.. data:: MISSING_PARAMS

    Indicates that the request is missing required parameters.
"""

【讨论】:

这是相关文档页面的当前永久链接:sphinx-doc.org/en/master/usage/restructuredtext/…【参考方案4】:

您可以使用哈希 + 冒号来记录属性(类或模块级别)。

#: Use this content as input for moo to do bar
MY_CONSTANT = "foo"

这将由 一些 文档生成器获取。

这里有一个例子,找不到更好的:Sphinx document module properties

【讨论】:

@ecatmur 的答案是 Sphinx 文档模块属性链接的示例吗?【参考方案5】:

我认为你在这里不走运。

Python 不直接支持变量上的文档字符串:没有属性可以附加到变量并以交互方式检索,例如模块、类和函数上的 __doc__ 属性。

Source.

【讨论】:

但是,如果 OP 只是为了 sphinx 而记录它们,我相信 epydoc 使用的 #: 在 sphinx 解析器中被重用了。 @mgilson:啊,那么链接的文档可能会有所帮助☺︎【参考方案6】:

以下内容适用于 Sphinx 2.4.4:

foo.py

API_ERROR = 1
"""int: Indicates some unknown error."""

然后记录它:

.. automodule:: foo.py 
    :members:

【讨论】:

【参考方案7】:

Sphinx Napoleon Python documentation extension 允许在属性部分记录模块级变量。

每https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_numpy.html:

Attributes
----------
module_level_variable1 : int
    Module level variables may be documented in either the ``Attributes``
    section of the module docstring, or in an inline docstring immediately
    following the variable.

    Either form is acceptable, but the two should not be mixed. Choose
    one convention to document module level variables and be consistent
    with it.

【讨论】:

【参考方案8】:

写作只是因为到目前为止我还没有在答案中看到这个选项:

您还可以将常量定义为在调用时简单地返回所需常量值的函数,例如:

def get_const_my_const() -> str:
    """Returns 'my_const'."""
    return "my_const"

这样一来,它们一方面会“更加稳定”(不必担心重新分配),而且它们还提供了定期记录文档的机会,就像任何其他功能一样。

【讨论】:

人们不赞成,但这当然是一个合理的选择。我认为没有 get_const_ 前缀会更干净,不过……如果 Python 也有模块级属性,那可能是另一个可行的选择。

以上是关于如何在 Python 中记录模块常量?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 rdoc 列出未记录的模块/类/常量/方法?

如何在Python中记录模块?

在 python 项目中如何记录日志

如何使用 Perl 模块中的常量?

如何覆盖模块中的类常量和方法?

如何在模块命名空间中使用 Vuex 类型常量?