PEP 0257 -- Docstring 约定

Python部落(www.freelycode.com)组织翻译, 禁止转载


概要


本篇PEP文档主要对Python函数文档字符串的语义和规范进行了相关阐述。


基本原理


PEP的目标是让程序猿写出的Python函数文档字符串(Doctring)高度结构化、规范化:文档字符串应该包含什么,如何书写(在文档字符里避免使用标记语法的情况下)。 PEP中包含的是约定俗成的东西,不是定律或严苛的语法。


一个通用的公约给编程提供了可维护性,清晰性,一致性,同时也提供了良好的编程习惯的基础。但它从来不会强迫你去按照它的标准做你不喜欢的事情。这就是Python!”

—Tim Peters on comp.lang.python, 2001-06-16


如果你不按约定编写代码,最坏的情况是,写出来的代码很邋遢。但有些软件代码的编写必须按照约定来(比如 Docutils [3] docstring 的处理系统[1] [2] )。总而言之,遵守约定,能给你带来最好的结果。.


详述


什么是 Docstring?


Docstring是模块、类、方法中的第一个声明性描述说明。比如docstring变成 __doc__ 类的special attribute of that object.


所有的modules一般都有Docstring,模块中的类和方法也应该有Doctring。公共方法 (包含 __init__ 构造器的方法) 也应该有Doctring。一个包的描述信息一般在包目录中文件__init__.py的Doctring中。


字符串类说明信息在Python代码中随处可见,通常用称为文档说明的一部分。这些信息不会被Python字节码编译器识别到,也不能被运行时对象属性访问。但是,有两类附加的docstring能被软件工具提取出来。

1. 在一个模块、类或者__init__ 方法中一开始就出现的字符串描述信息,我们称之为 "attribute docstrings"。

2. 紧跟在一个 docstring 后面的字符串描述信息,被称为 "additional docstrings"。


请参考 PEP 258 , "Docutils Design Specification" [2] ,里面有关于"attribute docstrings"和"additional docstrings"的详细说明。


下面是三种情况下,Docstring的一般写法:

  • 正常情况下: """三个双引号""" (单纯用三个双引号引起来)

  • 如果有反斜线: r"""raw triple double quotes"""  (在开头加字幕r)

  • 如果使用统一字符编码标准:u"""Unicode triple-quoted strings""" . (在开头加字幕u)


一共有两种Docstring,单行的和多行的:


单行 Docstrings


单行的字符串文档是最常见的,比如:

def kos_root():

    """Return the pathname of the KOS root directory."""

    global _kos_root

    if _kos_root: return _kos_root

    ...


注意:

就算只有一行,也要用三个双引号,方便后期扩展。


Docstring 正文前后的双引号都要在一行,这样更美观一些。

Docstring 前后都不要留空行。

Docstring 描述的是一个阶段性的结果。 它预期性的告诉我们某个方法的具体功能信息("执行某个任务"或"返回某类值"),不是描述;比如,不要这么写“返回 路径”。   ???


单行 docstring 的内容不能是对方法和参数简单的重复描述,不要像下面那样写:

def function(a, b):

    """function(a, b) -> list"""


内省机制:Python的内省机制还是比较强大的。所谓内省,实际上主要是指在runtime获得一个对象的全部类型信息。这样可以提供更好的编程灵活性。


这种类型的文档字符串只适合C函数(比如内嵌函数),因为C中没有自省的机制。然而,返回值的类型不能通过内省确定,因此应在Docstring中加以说明。因此,文档字符串的首选形式是这样的:

def function(a, b):

    """Do X and return a list."""

(Of course "Do X" should be replaced by a useful description!)


多行 Docstrings


多行 docstrings 首先包含一个概述行,紧跟着空一行,然后跟着更加详细的描述信息。概述行会被自动索引工具识别并处理,所以概述行必须是独自占用一行且和后续描述间空一行。概述行可以和开始的双引号在一行,也可以另起一行,独占第二行。整个Docstring的缩进都应该和第一行保持一致 (可以参考下面的例子)。


通常,在给一个类做文档注释时,在所有Docstring(单行和多行)的末尾插入一个空行,类的每个方法之间由一个空行分隔开,所以,需要在Docstring跟第一个方法之间留一个行空行。


一个独立可运行的脚本中的Docstring应当包含该脚本的使用说明信息。 调用脚本的过程中,如果参数用错或缺失,应能够打印错误信息 (或者用于 "-h" 选项,用来显示帮助信息).。这样的 docstring 应该描述出脚本的功能、命令行语法、环境变量和文件信息。 使用说明应尽可能详细和充分,能够指导新用户正确的使用脚本,同时对于高级用户而言,也应在该Docstring里获取到这个脚本的所有选项和参数说明。


一个模块的docstring应该列出该模块能够输出的类、异常、方法 (以及其他的一些对象)的对应简述信息,每个对象的描述独占一行。 一个包的docstring(比如docstring of the package's __init__.py module) 应该列出包的模块清单和子包清单。


一个方法的docstring应该简述它的功能、参数、返回值和类型、异常、约束等。还有可选参数。还应说明这些关键字参数是否属于接口的一部分。 


一个类的docstring应该包含这个类的功能概述、公共方法清单、实例变量清单。如果这个类设计的可以作为父类,而且给子类预留了相应的接口,那么,这个接口应该在docstring中单独列出。除此以外,这个类的构造方法的文档信息应该在 __init__ 方法的docstring中表述。具体到方法的话,各自应该有自己的docstring。


如果一个类是另一个类的子类,而且这个类的几乎完全继承了父类,那么要在它的docstirng中应该提到这些信息,以及与父类的不同之处。使用动词 "override" 指明某个子类的方法是重写了父类的。使用动词 "extend" 指明,子类的某个方法完全继承了父类的方法,并做了功能的扩展。


当连续的代码中的方法的参数包含大写的情况时,不要使用Emacs 编辑器的格式化功能。Python是大小写敏感的,参数名称即可被用作关键字参数,因此 docstring 中应该把参数名描述准确。最好是每行列一个参数。比如:

def complex(real=0.0, imag=0.0):

    """Form a complex number.


    Keyword arguments:

    real -- the real part (default 0.0)

    imag -- the imaginary part (default 0.0)

    """

    if imag == 0.0 and real == 0.0:

        return complex_zero

    ...

除非所有的Docstring内容正好占用一行的情况,才把闭合侧的双引号和开始的引号写在一行。如此一来,Emacs编辑器中的fill-paragraph命令就能用在这个地方了。


Docstring 缩进的处理


文档字符串处理工具会把Docstring的第二行开始到最后一行的缩进部分进行等长删除,删除部分的长度为这几行中最短的缩进值。Docstring第一行中缩进都是没有意义的,换行符也是,会被自动处理删除掉。后续代码行中的相对缩进则全部保留。Docstring前后的空白行不应保留。


代码比语言更精准,下面是这个算法的实现: 


def trim(docstring):

    if not docstring:

        return ''

    # Convert tabs to spaces (following the normal Python rules)

    # and split into a list of lines:

    lines = docstring.expandtabs().splitlines()

    # Determine minimum indentation (first line doesn't count):

    indent = sys.maxint

    for line in lines[1:]:

        stripped = line.lstrip()

        if stripped:

            indent = min(indent, len(line) - len(stripped))

    # Remove indentation (first line is special):

    trimmed = [lines[0].strip()]

    if indent < sys.maxint:

        for line in lines[1:]:

            trimmed.append(line[indent:].rstrip())

    # Strip off trailing and leading blank lines:

    while trimmed and not trimmed[-1]:

        trimmed.pop()

    while trimmed and not trimmed[0]:

        trimmed.pop(0)

    # Return a single string:

    return '\n'.join(trimmed)


样例中的docstring包含两个换行符,因此长度为3行。第1行和第3行为空行:


def foo():

    """

    This is the second line of the docstring.

    """


举例说明:


>>> print repr(foo.__doc__)

'\n    This is the second line of the docstring.\n    '

>>> foo.__doc__.splitlines()

['', '    This is the second line of the docstring.', '    ']

>>> trim(foo.__doc__)

'This is the second line of the docstring.'


经过裁剪处理后,下面两段代码的Docstring是等价的:


def foo():

    """A multi-line

    docstring.

    """


def bar():

    """

    A multi-line

    docstring.

    """


英文原文: https://www.python.org/dev/peps/pep-0257/

译者: cg2580

 

2月15日11:00到13:00网站停机维护,13:00前恢复