如何正确记录 **kwargs 参数?

136

我正在使用 Sphinxautodoc扩展 来生成Python模块的API文档。虽然我知道如何很好地记录特定的参数,但我找不到如何记录**kwargs参数的示例。

有没有人有一个清晰的记录这些内容的好示例?


这完全取决于您使用的文档字符串方法。(reStructuredText,Sphinx,Google) - Stevoisiak
4
这个问题不应该被关闭,它是一个有效的问题。它很具体(如何使用Sphinx文档化**kwargs),由于Python中的文档注释并不完全标准化,这可能会导致不同的观点或多种方法,只要它们能够特别支持这个问题(Sphinx)。 - JerodG
1
拜托了,不要使用 **kwargs。它缺乏清晰度,没有文档支持,也失去了 IDE 的支持。 - Basil Musa
参见:Python 字典解包 PEP448 - undefined
8个回答

71

在发现这个问题后,我采用了以下方法,它是有效的Sphinx并且运行得相当不错:

def some_function(first, second="two", **kwargs):
    r"""Fetches and returns this thing

    :param first:
        The first parameter
    :type first: ``int``
    :param second:
        The second parameter
    :type second: ``str``
    :param \**kwargs:
        See below

    :Keyword Arguments:
        * *extra* (``list``) --
          Extra stuff
        * *supplement* (``dict``) --
          Additional content

    """

r"""...""" 是必需的,以使它成为“原始”文档字符串,从而保留 \* (供 Sphinx 选择作为文字 * 而不是“强调”的开头)。

所选格式(带括号类型和 m-dash 分隔的项目列表)只是为了匹配 Sphinx 提供的自动格式化。

一旦您花费这样的努力使“关键字参数”部分看起来像默认的“参数”部分,那么从一开始就自己编写参数部分可能会更容易(如其他答案中所述),但作为概念验证,如果您已经使用 Sphinx,这是一种实现漂亮的补充 **kwargs 外观的方法之一。


拜托了,不要使用**kwargs。它缺乏清晰度,没有文档支持,失去了IDE的支持,而且容易混淆。 - Basil Musa
@BasilMusa 这可能需要在基类中使用,因为特定抽象函数的参数在不同的实现中可能会有所不同。 - Beefcake

44

使用Sphinx解析Google风格的文档字符串

免责声明:未经测试。

从这个Sphinx文档字符串示例的摘录中,*args**kwargs 保持未展开状态:

def module_level_function(param1, *args, param2=None, **kwargs):
    """
    ...

    Args:
        param1 (int): The first parameter.
        param2 (Optional[str]): The second parameter. Defaults to None.
            Second line of description should be indented.
        *args: Variable length argument list.
        **kwargs: Arbitrary keyword arguments.

我建议采用以下方法来实现紧凑性:

    """
    Args:
        param1 (int): The first parameter.
        param2 (Optional[str]): The second parameter. Defaults to None.
            Second line of description should be indented.
        *param3 (int): description
        *param4 (str): 
        ...
        **key1 (int): description 
        **key2 (int): description 
        ...

请注意,对于带有**key参数的情况,Optional不是必需的。

否则,您可以尝试在其他参数下显式列出*args,并在关键字参数下列出**kwargs(请参见文档字符串部分):

    """
    Args:
        param1 (int): The first parameter.
        param2 (Optional[str]): The second parameter. Defaults to None.
            Second line of description should be indented.
    
    Other Parameters:
        param3 (int): description
        param4 (str): 
        ...

    Keyword Args:
        key1 (int): description 
        key2 (int): description 
        ...

1
这段代码看起来语法有误。*args作为位置参数不能放在param2=None之后。 - Lohengrin
@Lohengrin 谢谢你指出来,已经修正。 - Oleg

16

在Sphinx的文档中有一个文档字符串示例。具体来说,他们展示了以下内容:

def public_fn_with_googley_docstring(name, state=None):
"""This function does something.

Args:
   name (str):  The name to use.

Kwargs:
   state (bool): Current state to be in.

Returns:
   int.  The return code::

      0 -- Success!
      1 -- No good.
      2 -- Try again.

Raises:
   AttributeError, KeyError

A really great idea.  A way you might use me is

>>> print public_fn_with_googley_docstring(name='foo', state=None)
0

BTW, this always returns 0.  **NEVER** use with :class:`MyPublicClass`.

"""
return 0

尽管您明确询问了,但我也想指出Google Python风格指南。他们的文档字符串示例似乎暗示他们没有特别调用kwargs。(other_silly_variable=None)
def fetch_bigtable_rows(big_table, keys, other_silly_variable=None):
"""Fetches rows from a Bigtable.

Retrieves rows pertaining to the given keys from the Table instance
represented by big_table.  Silly things may happen if
other_silly_variable is not None.

Args:
    big_table: An open Bigtable Table instance.
    keys: A sequence of strings representing the key of each table row
        to fetch.
    other_silly_variable: Another optional variable, that has a much
        longer name than the other args, and which does nothing.

Returns:
    A dict mapping keys to the corresponding table row data
    fetched. Each row is represented as a tuple of strings. For
    example:

    {'Serak': ('Rigel VII', 'Preparer'),
     'Zim': ('Irk', 'Invader'),
     'Lrrr': ('Omicron Persei 8', 'Emperor')}

    If a key from the keys argument is missing from the dictionary,
    then that row was not found in the table.

Raises:
    IOError: An error occurred accessing the bigtable.Table object.
"""
pass

A-B-B对于引用子进程管理文档的接受答案有疑问。如果您导入一个模块,可以通过inspect.getsource快速查看模块docstrings。
以下是使用Silent Ghost的建议在Python解释器中的示例:
>>> import subprocess
>>> import inspect
>>> import print inspect.getsource(subprocess)

当然,您也可以通过帮助函数查看模块文档。例如:help(subprocess)。
个人不太喜欢subprocess的docstring关于kwargs的示例,但是像Google示例一样,它并没有像Sphinx文档示例中那样单独列出kwargs。
def call(*popenargs, **kwargs):
"""Run command with arguments.  Wait for command to complete, then
return the returncode attribute.

The arguments are the same as for the Popen constructor.  Example:

retcode = call(["ls", "-l"])
"""
return Popen(*popenargs, **kwargs).wait()

我将包含对A-B-B问题的回答,因为值得注意的是,您可以通过这种方式查看任何模块的源代码或文档,以获取有关如何评论您的代码的见解和灵感。

2
更正:这不是Sphinx文档的一部分,而是一个独立的“示例pypi项目”的一部分,它明确将自己描述为非权威教程。 - boycy
3
other_silly_variable不是一个kwargs参数,而是一个完全正常的参数。 - bugmenot123

8
如果您想以numpydoc风格执行此操作,您可以在参数部分简单地提到**kwargs而不指定类型 - 正如来自sphinx扩展napolean的numpydoc example和pandas文档协作2018年的docstring guide所演示的那样。
这里有一个我从LSST developer guide中找到的例子,非常好地解释了应该是**kwargs参数的描述。
def demoFunction(namedArg, *args, flag=False, **kwargs):
    """Demonstrate documentation for additional keyword and
    positional arguments.

    Parameters
    ----------
    namedArg : `str`
        A named argument that is documented like always.
    *args : `str`
        Additional names.

        Notice how the type is singular since the user is expected to pass individual
        `str` arguments, even though the function itself sees ``args`` as an iterable
        of `str` objects).
    flag : `bool`
        A regular keyword argument.
    **kwargs
        Additional keyword arguments passed to `otherApi`.

        Usually kwargs are used to pass parameters to other functions and
        methods. If that is the case, be sure to mention (and link) the
        API or APIs that receive the keyword arguments.

        If kwargs are being used to generate a `dict`, use the description to
        document the use of the keys and the types of the values.
    """

或者,基于@Jonas Adler的建议,我认为最好将**kwargs及其描述放在其他参数部分 - 即使matplotlib文档指南中的示例也建议这样做。


7

如果还有其他人在寻找有效的语法,这里有一个示例文档字符串。这只是我自己的做法,希望对你有用,但我不能声称它符合任何特定的标准。

def bar(x=True, y=False):
    """
    Just some silly bar function.

    :Parameters:
      - `x` (`bool`) - dummy description for x
      - `y` (`string`) - dummy description for y
    :return: (`string`) concatenation of x and y.
    """
    return str(x) + y

def foo (a, b, **kwargs):
    """
    Do foo on a, b and some other objects.

    :Parameters:
      - `a` (`int`) - A number.
      - `b` (`int`, `string`) - Another number, or maybe a string.
      - `\**kwargs` - remaining keyword arguments are passed to `bar`

    :return: Success
    :rtype: `bool`
    """
    return len(str(a) + str(b) + bar(**kwargs)) > 20

4
那个个别的关键字参数呢? - maasha
1
我经常使用这种约定,提到它们被传递给哪个函数,但将定义留给拥有它们的函数。然后,当它们被重构时,多个文档字符串不需要更新。 - Excel Help

5

我无法找到文档的实际链接,但这个可以使用(使用 Sphinx 3.4.3):

class Foo:
    """A Foo implementation

    :param str foo: Foo
    :param int bar: Bar
    :keyword str key1: kwarg 1
    :keyword str key2: kwarg 2
    :keyword int key3: kwarg 3
    """

    def __init__(self, foo, bar, **kwargs):
        pass

在 https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#info-field-lists 的文本中提到了 keyword - James McKinney
在这种情况下,带有签名“*args,**kwargs”的函数怎么样? '*args'应该如何记录? - Anish Ramaswamy

5
这取决于您使用的文档风格,但如果您使用 numpydoc 风格,建议使用 Other Parameters 来记录 **kwargs
例如,按照 quornian 的示例:
def some_function(first, second="two", **kwargs):
    """Fetches and returns this thing

    Parameters
    ----------
    first : `int`
        The first parameter
    second : `str`, optional
        The second parameter

    Other Parameters
    ----------------
    extra : `list`, optional
        Extra stuff. Default ``[]``.
    suplement : `dict`, optional
        Additional content. Default ``{'key' : 42}``.
    """

请特别注意,建议提供kwargs的默认值,因为这些值在函数签名中不明显。

3
我不确定你的建议是基于旧文档还是个人经验,但你所提供链接的当前“其他参数”文档指出,“应用于描述很少使用的参数”,且“仅在函数具有大量关键字参数时使用,以避免混乱的参数部分”。 - Ninjakannon

-9
我认为 subprocess 模块文档 是一个很好的例子。列出所有 顶级/父类 的详尽参数列表。然后,对于所有其他 **kwargs 的出现,只需参考该列表即可。

115
只有我一个人对这个答案感到无解吗?我找不到具体的例子。 - Asclepius
2
示例可能是 subprocess.call(*popenargs, **kwargs)。文档中记述为 subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False),其中 * 之后的所有内容都是在 **kwargs 中识别的键(或至少是常用的键)。 - nos
2
那个最有意义的补充现在是subprocess.Popen,但我不确定它现在是否还是一个特别好的例子。 - Donal Fellows
除非我错了,否则在Python 3.7中已不再记录。 - Mateen Ulhaq
21
因未在回答中提供实际示例而被投反对票。 - naught101

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接