Skip to main content

PEP257-如何规范书写文档说明

总结

在编写函数时,要养成写文档字符串的习惯,并在编写函数代码之前尝试写一下。

如果你不能写一个清晰的文档字符串来描述函数做什么,就说明你需要再考虑考虑为什么要写这个函数了。

  • 每个函数都需要有一个文档字符串
  • 使用适当的语法和标点符号;用完整的句子写
  • 首先对函数的作用进行一句话的总结
  • 使用说明性语言而不是描述性语言

引言

这个 PEP 文件是关于 Python 文档说明的书写语义和约定。

理论基础

这个 PEP 协议的目的是标准化文档说明的高级结构:里面有个包含说明,怎么去描述(不在其内创建任何的标记语法)。这个 PEP 秀逸包含约定,而不是法规或语法。


"有个通用的约定应该是可维护的、清晰可懂的、满足一致性的,同时也应该是好的编程习惯的基础。它不会违背你的意愿来强制要求你遵循那些规则。这就是 Python!" -- Tim Peter 写于 comp.lang.python,2001-06-16

不按照约定来书写,最差的结果是你写的代码会比较难看,不美观。同时有些软件对约定(规范)比较重视,遵循这些规范才能得到好的结果。

详细说明

什么是文档说明?

一个文档说明是一个字符串,它出现在一个模块,函数,类或方法的定义的第一个语句。这样的文档说明即为该对象的doc特殊属性。

所有的模块,或对于被一个模块导入的函数和类都应该有文档说明。公共方法(包括init构造函数)也应该有文档说明。一个包可以在其包目录下存放其(模块的文档说明)init.py 文件。

说明字符串通常出现在 Python 代码中的任意位置,这个代码也可以看做文档。他们既不能由 Python 编译器识别,也无法向运行时的对象属性那些被访问(即不分配给doc),但两种额外的文档说明,可以通过软件工具提取:

  1. 在一个模块、类或init方法的顶层的一个简单申明后立即出现的字符串 --“属性文档说明”
  2. 在其他文档说明后立即出现的字符串 --“补充文档说明”

想获取关于属性和补充文档说明的详细描述,请查阅 PEP 258,"Docutils Design Specification"(文档工具设计说明)

XXX 提及的 2.2 版本的文档说明特性

为了满足一致性,常用 3 个双引号 """quotes""" 来包裹文档说明。如果你在文档说明里使用了反斜杠 \,使用 r"""quotes"""" 来包裹。对于那些使用了 Unicode 文档说明的,使用 u""""quotes""" 来包裹。

有 2 种形式的文档说明:单行和多行文档说明。

单行文档说明

单行的说明文档是十分明显的一种类型。将说明描述字符串写入一行即可。比如说:

def kos_root():
"""Return the pathname of the KOS root directory."""
global _kos_root
if _kos_root: return _kos_root
...

说明:

  • 三个引号使用频繁,即便字符串可以写在一行内。这易于后期扩展。
  • 单行的 3 个双引号应在一行,看上去更美观
  • 在文档说明字符串前后没有空行
  • 文档说明应以句号结尾的一段语句。它以一个命令规定了函数或方法的结果,而不是描述;例如,不要写 “Returns the pathname ...”
  • 单行文档说明不应该时一个 "说明" 去重申函数 / 方法的参数。不要像这样:
def function(a, b):
"""function(a, b) -> list"""

上面的这种类型的文档说明只适用于 c 函数 (例如内建函数) 这样的无法内省的函数。然而,return 值的类型无法由内省决定,因此这需要被注意。对于这种文档说明较好的形式应该是:

def function(a, b):
"""Do X and return a list."""
多行文档说明

多行文档说明包含一个概要行(如单行文档说明那样),然后接一个空行,然后是更详细的说明。这种概要行应该使用动态索引工具来使用;让它在一行显示是十分重要的,并且使用空行来与其他的文档说明分割开。这个概要行可能与右边的 3 个双引号在同一行或在下一行。整个文档说明跟第一行那样缩进排版。

在所有的文档说明后插入一个空白行(一行或多行),类文档 —— 一般来说,类的方法是由一个单一的空行隔开,文档说明需要有一个空行来开始第类的一个方法。

当一个脚本调用时使用了不正确或缺少参数(或是一个 “H” 选项,在 “帮助”),其文档说明(一个独立的程序)应打印其使用方法信息。这样的文档说明应该包含脚本的功能和命令行语法、环境变量和文件。使用信息可以相当详细(有几个屏幕满),对于新用户正确使用命令,以及对高级用户的所有选项和参数的完正的快速引用应该足够了。

一个模块的文档说明一般应列出由模块导入的类,异常和功能(以及任何其他对象),并对应的使用一个单行的概要。(这些概要一般会比对象的文档说明提供更少的细节)。一个包的文档说明(即包的init .py 模块的文档说明),还应该列出由包导入的的模块和子包。

一个函数或方法的文档说明应该总结其行为和记录其参数,返回其值(S),附带作用,抛出的异常和调用时的限制(所有这些如果适用)。可选参数应被表示。无论关键字参数是否为接口的一部分它应该记录。

一个类的文档说明应该总结其行为并列出其公共方法和实例变量。如果这个类是一个子类,有额外的接口,该接口应分别列出(在文档字符串)。类的构造函数应该记录在文档说明的init方法。单独的方法应该由自己的文档说明记录。

如果一个类的子类及其行为主要是继承自该类,它的文档说明应该提到这点并总结其差异。用动词 “override” 表明一个类方法代替父类的方法并且它不调用父类的方法;用动词 “extend”,表示一个子类的方法调用父类的方法(除了它自己的行为)。

不要在运行的文本中使用 Emacs 的约定方法大写函数或方法的参数。Python 是大小写敏感,并且参数名可以使用关键字参数,所以文档说明应该记录正确的参数名。最好是在一个单独的行中列出每个参数。例如:

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

除非整个文档说明在一行中,否则将后面的引号单独放一行。可以使用 Emacs 的 fill-paragraph 命名创建中格式。

处理文档说明的缩进

文档说明处理工具将文档说明的第二行和之后的行的缩进除去,等于将第一行后的所有非空白行的碎金最小化。第一行的任意缩进(即到第一个换行符)是不明显的,要被删除的。文档说明的后面的行的相对缩进保留。空白行应该从文档的开始和结束处删除。

由于代码比文字更精确,这里是算法的实现:

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)

在这个例子中的文档说明串包含两个换行符并因此有 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.'

下面的 2 个函数的文档说明是一致的:

def foo():
"""A multi-line
docstring.
"""

def bar():
"""
A multi-line
docstring.
"""

参考和补充说明

序号说明及连接
[1]PEP 256, Docstring Processing System Framework, Goodger (http://www.python.org/dev/peps/pep-0256/)
[2](1, 2) PEP 258, Docutils Design Specification, Goodger (http://www.python.org/dev/peps/pep-0258/)
[3]http://docutils.sourceforge.net/
[4]http://www.python.org/dev/peps/pep-0008/
[5]http://www.python.org/sigs/doc-sig/

版权

本文档在公共域中存放

鸣谢

这个 “规范” 的文字大多来自于 Python Style Guide [4],由 Guido van Rossum 编写。

本文借鉴了 Python Doc-SIG [5] 文档。感谢过去和现在的所有成员。

来源:https://github.com/python/peps/blob/master/pep-0257.txt