Python 文档字符串模板化

Python docstrings templated

为什么动态格式化文档字符串不起作用? 在函数定义时是否有可接受的解决方法?

>>> DEFAULT_BAR = "moe's tavern"
>>> def foo(bar=DEFAULT_BAR):
...     """
...     hello this is the docstring
...     
...     Args:
...       bar (str)    the bar argument (default: {})
...     """.format(DEFAULT_BAR)
... 
>>> foo.__doc__
>>> foo.__doc__ is None
True

我尝试使用老式风格的 %s 格式,但也没有用。

您的字符串需要调用函数,但创建函数时并未执行函数体。

正确的文档字符串不会执行,它只是从解析的源代码中获取并附加到函数对象,没有为此执行任何代码。 Python 将文档字符串存储为代码对象中的第一个常量值:

>>> def f():
...     """docstring"""
...     pass
...
>>> f.__code__.co_consts
('docstring', None)

在构造新函数时将代码对象传递给函数类型的位置(参见PyFunction_New() function)。

参见Function definitions reference documentation

The function definition does not execute the function body; this gets executed only when the function is called. [3]

[...]

[3] A string literal appearing as the first statement in the function body is transformed into the function’s __doc__ attribute and therefore the function’s docstring.

您的定义在其他方面是有效的;函数体顶部没有独立的字符串文字。您的字符串文字是函数本身的一部分,并且仅在调用该函数时执行(并且由于您不存储该结果而将其丢弃)。

请注意,函数对象的 __doc__ 属性是可写的;您始终可以在 创建函数后 应用变量:

>>> DEFAULT_BAR = "moe's tavern"
>>> def foo(bar=DEFAULT_BAR):
...     """
...     hello this is the docstring
...
...     Args:
...       bar (str)    the bar argument (default: {})
...     """
...
>>> foo.__doc__ = foo.__doc__.format(DEFAULT_BAR)
>>> print(foo.__doc__)

    hello this is the docstring

    Args:
      bar (str)    the bar argument (default: moe's tavern)

你可以在 functionobject.__globals__inspect.getargspec() 的帮助下在装饰器中做到这一点,但随后在模板中使用命名插槽,这样你就可以将所有内容作为字典应用并拥有文档字符串选择要插入的内容:

from inspect import getargspec

def docstringtemplate(f):
    """Treat the docstring as a template, with access to globals and defaults"""
    spec = getargspec(f)
    defaults = {} if not spec.defaults else dict(zip(spec.args[-len(spec.defaults):], spec.defaults))
    f.__doc__ = f.__doc__ and f.__doc__.format(**dict(f.__globals__, **defaults))
    return f

演示:

>>> @docstringtemplate
... def foo(bar=DEFAULT_BAR):
...     """
...     hello this is the docstring
...
...     Args:
...       bar (str)    the bar argument (default: {bar!r}, or {DEFAULT_BAR!r})
...
...     """
...
>>> print(foo.__doc__)

    hello this is the docstring

    Args:
      bar (str)    the bar argument (default: "moe's tavern", or "moe's tavern")

函数关键字参数覆盖全局变量,就像它们在函数中一样。

尝试这样的事情(建议@user2357112):

#!python3

def FORMAT_DOC(f):
    """Decorator to format docstring of a function. Supplies 
    `defaults` dictionary, positional values, and argname:value 
    pairs to format - use {defaults[a]} or {a} or {0} to access
    the 0th default value, etc.
    """
    defaults = f.__defaults__
    docs = f.__doc__

    if docs and defaults:
        nargs = f.__code__.co_argcount
        argnames = f.__code__.co_varnames[nargs-len(defaults):nargs]
        argdefaults = dict(zip(argnames, defaults))
        f.__doc__ = docs.format(defaults=argdefaults, *defaults, **argdefaults)

    return f

@FORMAT_DOC
def f(a):
    pass

@FORMAT_DOC
def f(a,b,c=1,d=2):
    """Docstring

    By default, c = {} and d = {}
    """
    v=a+c
    w=b+d
    x=w/v
    return x+1

@FORMAT_DOC
def f(a=0, b="foo", c="bar"):
    """Docstring: a={0}, b={defaults[b]}, c={c}"""
    pass