【问题标题】:More than 1 docstrings for a single module/function etc.?单个模块/功能等超过 1 个文档字符串?
【发布时间】:2010-02-13 19:13:55
【问题描述】:

我正在使用 python 3.1。

是否可以为单个模块或函数创建超过 1 个文档字符串? 我正在创建一个程序,并且我打算有多个文档字符串,每个文档字符串都有一个类别。我打算将程序提供给其他人以便他们可以使用它,并且为了让程序员和非程序员都可以轻松使用,我在程序本身中引用了文档字符串以供文档使用。

更具体地说,我在程序/模块中有一个菜单作为界面,其中一个选项将允许访问模块文档字符串以获取程序文档。因此,如果可能的话,我想制作多个文档字符串来对不同类型的文档进行分类。因此,如果用户想查看文档的某些部分,他们会更容易。

例如。第一个文档字符串包含有关如何使用该程序的说明。第二个文档字符串包含有关程序的一部分如何工作的信息。第三个文档字符串包含有关另一部分如何工作的信息。等等

这可能吗?如果是这样,您如何引用它们?

更新:添加评论。

我最初的想法是实际上有多个文档字符串:

def foo():
    """docstring1: blah blah blah"""
    """docstring2: blah blah blah"""
    pass # Insert code here

然后我可以使用一些代码来引用这些文档字符串中的每一个。 那么,我猜那是不可能的?

【问题讨论】:

  • 那么......一般来说,我的问题的解决方案是不使用实际的文档字符串,而是使用其他东西,比如变量,来包含我需要的任何文档,或者制作一个巨大的包罗万象的文档docstring并与模块中的代码进行分隔?
  • 也许您有不同的解决方案?愿意分享吗?两者看起来都与您的原始提案非常相似。
  • 我无法在此处的评论中添加代码,因此我编辑了问题以包含它。

标签: python docstring


【解决方案1】:

我不建议尝试使用文档字符串做一些复杂的事情。最好保持文档字符串简单,如果您想提供一堆不同的文档选项,请做其他事情。

如果你真的想做你所描述的,我建议你使用标签来分隔文档字符串中的部分。像这样:

def foo(bar, baz):
    """Function foo()

* Summary:
    Function foo() handles all your foo-ish needs.  You pass in a bar and a baz and it foos them.

* Developers:
    When you change foo(), be sure you don't add any global variables, and don't forget to run the unit tests.

* Testers:
    When you test foo, be sure to try negative values for baz.
"""
    pass # code would go here

然后你可以很容易地将你的字符串分割成块,当用户选择一个菜单项时,只显示适当的块。

s = foo.__doc__  # s now refers to the docstring

lst = s.split("\n* ")
section = [section for section in lst if section.startswith("Developers")][0]
print(section) # prints the "Developers" section

这样,当你在交互式 Python shell 中工作时,你可以说“help(foo)”,你会看到所有的文档字符串。而且,您并没有改变 Python 基本部分的基本行为,这会吓坏其他试图研究您的代码的人。

您还可以做一些更简单的事情:只需为不同目的制作一个包含文档字符串的大型全局字典,然后根据每个新事物的源代码对其进行更新。

doc_developers = {} doc_testers = {}

def foo(bar, baz):
    """Function foo()

Function foo() handles all your foo-ish needs.  You pass in a bar and a baz and it foos them."
    pass # code goes here

doc_developers["foo"] = "When you change foo(), be sure you don't add any global variables, and don't forget to run the unit tests."

doc_testers["foo"] = "When you change foo(), be sure you don't add any global variables, and don't forget to run the unit tests."

我最不喜欢的一点是,如果您更改函数 foo 的名称,则需要在多个地方进行更改:一次在实际的 def 中,一次在每个字典更新行中。但是你可以通过编写一个函数来解决这个问题:

def doc_dict = {} # this will be a dict of dicts
doc_dict["developers"] = {}
doc_dict["testers"] = {}

def doc_update(fn, d):
    name = fn.__name__
    for key, value in d.items():
        doc_dict[key][name] = value

def foo(bar, baz):
    """Function foo()

Function foo() handles all your foo-ish needs.  You pass in a bar and a baz and it foos them."
    pass # code goes here

d = { "developers": "When you change foo(), be sure you don't add any global variables, and don't forget to run the unit tests.",
"testers": " When you test foo, be sure to try negative values for baz."}

doc_update(foo, d)

可能有办法将 doc_update() 变成装饰器,但我现在没时间了。

【讨论】:

  • 使用 inspect.cleandoc 这样你就可以像其他代码一样通过缩进来“自然地”编写文档字符串。
【解决方案2】:

您想考虑使用 decorators 干净利落地执行 ~unutbu 建议的函数:为每个函数添加一个单独的字段。例如:

def human_desc(description):
    def add_field(function):
        function.human_desc = description
        return function
    return add_field

这就是human_desc 的实际效果:

@human_desc('This function eggfoobars its spam.')
def eggfoobar(spam):
    "Apply egg, foo and bar to our spam metaclass object stuff."
    print spam

解释

作为the doc explains,那段代码等价于:

def eggfoobar(spam):
    "Apply egg, foo and bar to our spam metaclass object stuff."
    print spam
eggfoobar = human_desc('This function eggfoobars its spam.')(eggfoobar)

human_desc('This function eggfoobars its spam.') 返回以下函数:

def add_field(function):
    function.human_desc = 'This function eggfoobars its spam.'
    return function

如您所见,human_desc 是一个函数,它为您作为参数传递的description 的值生成上述装饰器。装饰器本身是一个函数,它接受要装饰(修改)的函数并将其返回装饰(在这种情况下,即添加了那一点额外的元数据)。简而言之,这相当于:

def eggfoobar(spam):
    "Apply egg, foo and bar to our spam metaclass object stuff."
    print spam
eggfoobar.human_desc = 'This function eggfoobars its spam.'

然而,语法更简洁,更不容易出错。

显然,无论哪种方式,你得到的是:

>>> print eggfoobar.human_desc
This function eggfoobars its spam.

【讨论】:

    【解决方案3】:

    您可以使用定义了usageextra 属性的类,而不是使用函数。例如,

    class Foo(object):
        '''Here is the function's official docstring'''
        usage='All about the usage'
        extra='How another part works'
        def __call__(self):
            # Put the foo function code here
            pass
    foo=Foo()
    

    你可以像往常一样调用它:foo(), 你可以得到官方的文档字符串,以及像这样的备用文档字符串:

    print foo.__doc__
    print foo.usage
    print foo.extra
    

    你也可以为普通函数附加额外的属性(而不是像我上面那样使用一个类),但我认为语法有点丑:

    def foo():
        pass
    foo.usage='Usage string'
    foo.extra='Extra string'
    

    而且,模块也是对象。他们可以很容易地拥有额外的属性:

    如果你定义了模块常量

    USAGE='''blah blah'''
    EXTRA='''meow'''
    

    那么当你导入模块时:

    import mymodule
    

    您可以使用

    访问官方和备用文档字符串
    mymodule.__doc__
    mymodule.USAGE
    mymodule.EXTRA
    

    【讨论】:

    • +1 用于使用属性,但我想我会使用装饰器 - 特别是对于函数,因为它将信息保存在它所属的声明附近。
    • 如果 OP 的问题确实需要一个函数来保存额外的文档字符串,那么创建一个具有使用属性或方法的可调用类似乎是最易读的解决方案。有一个很长的使用字符串作为装饰器的参数可能看起来像一个巨大的囊肿:)
    • 你的提议看起来有问题的一件事是[item for item in dir((lambda: 2)) if item not in dir((foo))] 告诉我foo 缺失:__closure____code____defaults____get____globals____name__func_closurefunc_codefunc_defaultsfunc_dictfunc_docfunc_globalsfunc_name。我想这些对于典型的使用都不重要,但是当你最不期待的时候,它们已经准备好在阿尔卑斯山上咬你了。您不能显式子类化函数。这就是为什么我认为类不是这里工作的正确工具的主要原因。
    • @bp:OP 说他有一个调用这个函数的菜单项,他想要一个用于这种情况的特殊版本的文档字符串。在我看来,他想要的不仅仅是一个普通的函数,他想要一个至少可以做两件事的对象:被调用,并返回一个特殊的文档字符串。这就是为什么我认为一个类是合适的。我们没有理由必须坚持使用函数。
    • @bp:我一直在想你写的东西,恐怕我不明白你所说的“foo is missing”是什么意思,也不明白它怎么会“咬我”在阿尔卑斯山”(哎哟!)。请解释一下好吗?
    【解决方案4】:

    如果您想拥有多个可能的文档字符串,您可以替换 __doc__ 属性,但请考虑使初始文档字符串对所有类型都足够灵活。

    【讨论】:

    • 感谢快速回复,但这是如何工作的,我该如何替换 doc 属性?文档字符串不是在每个模块/函数等的开头使用第一组三引号创建的,并且 doc 引用它吗?
    • 验证 Python 中的少数属性是不可变的,甚至 __class__ 也不行。只需将新字符串绑定到属性即可。
    【解决方案5】:

    模块是类/函数/模块的集合。所以它的文档字符串给出了它包含的内容的介绍。

    类文档字符串说明了类是关于什么的,它的方法文档字符串说明了方法是什么。一个类有一个目的,一个方法只做一件事,所以它们应该有一个文档字符串。

    函数只做一件事,所以一个文档就足够了。

    我看不出多个文档字符串能满足什么目的。也许你的模块很大。划分为子模块,并在模块的文档字符串中提及子模块。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-03
      • 2012-03-19
      • 1970-01-01
      相关资源
      最近更新 更多