¿Cuál es la forma correcta de documentar un parámetro ** kwargs?


99

Estoy usando sphinx y el complemento autodoc para generar documentación API para mis módulos de Python. Si bien puedo ver cómo documentar bien parámetros específicos, no puedo encontrar un ejemplo de cómo documentar un **kwargsparámetro.

¿Alguien tiene un buen ejemplo de una forma clara de documentarlos?


Esto depende completamente del método de cadena de documentos que utilice. (reStructuredText, Sphinx, Google)
Stevoisiak

2
Esto no debería haberse cerrado. Es una pregunta válida. Es específico (cómo documentar ** kwargs usando sphinx) Como los comentarios de documentos no están completamente estandarizados en Python, esto dará lugar a opiniones (o múltiples métodos) siempre que apoyen la pregunta específicamente (sphinx).
JerodG

Respuestas:


5

Creo que subprocess-module's docs es un buen ejemplo. Proporcione una lista exhaustiva de todos los parámetros para una clase principal / principal . Luego, consulte esa lista para todas las demás apariciones de **kwargs.


97
¿Soy el único para quien esta respuesta no tiene sentido? No pude encontrar el ejemplo específico en cuestión.
Acumenus

2
El ejemplo es probable subprocess.call(*popenargs, **kwargs). Está documentado como subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False)donde todo después de *son las claves reconocidas en **kwargs(O al menos las que se usan con frecuencia)
nos

2
La continuación más significativa de eso es ahora subprocess.Popeny ya no estoy seguro de que sea un ejemplo particularmente bueno.
Donal Fellows

A menos que me equivoque, ya no está documentado en Python 3.7 .
Mateen Ulhaq

10
Votar en contra por no incluir un ejemplo real en la respuesta.
naught101

51

Después de encontrar esta pregunta, me decidí por lo siguiente, que es Sphinx válido y funciona bastante bien:

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

    """

Se r"""..."""requiere para hacer de esto una cadena de documentos "sin procesar" y así mantener la \*intacta (para que Sphinx lo tome como un literal *y no como el comienzo del "énfasis").

El formato elegido (lista con viñetas con tipo entre paréntesis y descripción separada por m-guiones) es simplemente para coincidir con el formato automático proporcionado por Sphinx.

Una vez que haya realizado este esfuerzo de hacer que la sección "Argumentos de palabras clave" se vea como la sección "Parámetros" predeterminada, parece que podría ser más fácil implementar su propia sección de parámetros desde el principio (según algunas de las otras respuestas) , pero como prueba de concepto, esta es una forma de lograr una apariencia agradable para los complementarios **kwargssi ya está usando Sphinx.


26

Cadenas de documentos de Google Style analizadas por Sphinx

Descargo de responsabilidad: no probado.

A partir de este recorte del ejemplo de la cadena de documentación de la esfinge , *argsy **kwargsse dejan sin expandir :

def module_level_function(param1, param2=None, *args, **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.

Me gustaría sugerir la siguiente solución de compacidad:

    """
    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 
        ...

Observe cómo Optionalno se requiere para los **keyargumentos.

De lo contrario , puede intentar enumerar explícitamente los * args debajo Other Parametersy **kwargsdebajo de Keyword Args(consulte las secciones analizadas ):

    """
    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 
        ...

9

Hay un ejemplo de doctstring para Sphinx en su documentación. Concretamente muestran lo siguiente:

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

Aunque preguntaste sobre explícitamente, también señalaría la Guía de estilo de Google Python . Su ejemplo de cadena de documentos parece implicar que no llaman específicamente a los kwargs. (other_silly_variable = Ninguno)

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

ABB tiene una pregunta sobre la respuesta aceptada de hacer referencia a la documentación de gestión de subprocesos. Si importa un módulo, puede ver rápidamente las cadenas de documentos del módulo a través de inspect.getsource.

Un ejemplo del intérprete de Python que utiliza la recomendación de Silent Ghost:

>>> import subprocess
>>> import inspect
>>> import print inspect.getsource(subprocess)

Por supuesto, también puede ver la documentación del módulo a través de la función de ayuda. Por ejemplo ayuda (subproceso)

Personalmente, no soy un fanático de la cadena de documentos de subproceso para kwargs como ejemplo, pero al igual que el ejemplo de Google, no enumera los kwargs por separado, como se muestra en el ejemplo de documentación de Sphinx.

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()

Incluyo esta respuesta a la pregunta de ABB porque vale la pena señalar que puede revisar la fuente o la documentación de cualquier módulo de esta manera para obtener información e inspiración para comentar su código.


2
Corrección: eso no es parte de la documentación de Sphinx, sino de un 'proyecto de ejemplo de pypi' independiente, que se describe explícitamente a sí mismo como un tutorial no autorizado.
boycy

other_silly_variableno es un argumento de kwargs, sino uno completamente normal.
bugmenot123

4

Si alguien más está buscando una sintaxis válida ... Aquí hay una cadena de documentos de ejemplo. Así es como lo hice, espero que te sea útil, pero no puedo afirmar que cumpla con nada en particular.

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

3
Entonces, ¿qué pasa con los argumentos de palabras clave individuales?
maasha

4

Esto depende del estilo de documentación que use, pero si está usando el estilo numpydoc , se recomienda documentar **kwargsusando Other Parameters.

Por ejemplo, siguiendo el ejemplo de 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}``.
    """

Tenga en cuenta especialmente que se recomienda dar los valores predeterminados de kwargs, ya que estos no son obvios a partir de la firma de la función.


1
No estoy seguro de si su sugerencia proviene de documentos anteriores o de su experiencia personal, pero la documentación actual de "Otros parámetros" (a la que se vincula) establece que debe "usarse para describir parámetros que se usan con poca frecuencia" y que "solo debe usarse si una función tiene una gran cantidad de parámetros de palabras clave, para evitar saturar la sección de parámetros ".
Ninjakannon

1

Si está buscando cómo hacer esto en estilo numpydoc , simplemente puede mencionarlo **kwargsen la sección Parámetros sin especificar el tipo , como se demuestra en el ejemplo de numpydoc de la extensión de esfinge napolean y la guía de cadena de documentos de la documentación de pandas sprint 2018.

Aquí hay un ejemplo que encontré en la guía para desarrolladores de LSST que explica muy bien cuál debería ser la descripción del **kwargsparámetro:

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.
    """

Alternativamente, basándose en lo que sugirió @Jonas Adler, creo que es mejor poner **kwargsy su descripción en la Other Parameterssección ; incluso este ejemplo de la guía de documentación de matplotlib sugiere lo mismo.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.