介绍

本文提供Python的C实现的C代码的编码风格。Python代码的风格指南参见相关信息的PEP[1]

注意,规则有时也会被打破。两个打破特定规则的好的理由:

  1. 当遵循这个规则回事代码更少地可读,即使其他人依据此规则去阅读代码。
  2. 未保持和周围也打破规则的代码一致(也许是历史原因)–尽管有一个机会去清理别人的烂摊子(以真正的XP格式)

C方言

  • Python 3.6之前使用的是ANSI/ISO标准C(1989版标准)。这意味着(以及其他许多情况)所有的申明都必须在代码块顶部(不需要再函数的顶部)。
  • Python 3.6及之后的版本使用采用了部分C99特性的C89:
    • 标准整形在<stdint.h><inttypes.h>中。我们要求定宽的整形类型。
    • static inline函数
    • 指定初始化器(designated initializers),对类型定义特别好。
    • 混合申明
    • 布尔值
    • C++风格的行注释
      未来C99的特性可能会被添加到这个列表视编译器支持而定(主要是MSVC)。
  • 不要使用GCC拓展(例如,不要写没有行尾的反斜杠的多行字符串)
  • 所有的函数申明和定义必须使用完全原型(例如,指定所有参数的类型)
  • 永远不要使用C++风格的//一行注释
  • 在几种主要编译器(gcc,VC++,一些其他的)上没有警告

代码布局

  • 使用4个空格缩进并完全禁用tab。
  • 每行不得超过79个字符。如果这一条和前一条一起没有给你的代码足够的空间,那么你的代码就太复杂了–考虑使用子程序。
  • 函数定义格式:函数名在第1列,最外面的花括号在第一列,在本地变量申明之后留空行。
1
2
3
4
5
6
7
8
9
10
static int
extra_ivars(PyTypeObject *type, PyTypeObject *base)
{
int t_size = PyType_BASICSIZE(type);
int b_size = PyType_BASICSIZE(base);
assert(t_size >= b_size); /* type smaller than base! */
...
return 1;
}
  • 代码结构:在诸如iffor等关键词和接下来的左括号直接一个空格;在括号内没有空格;大括号被强烈推荐使用但可在C允许的情况下省略,并且它们应格式化成下面所示:
1
2
3
4
5
6
if (mro != NULL) {
...
}
else {
...
}
  • return语句不应有多余的括号:
    return Py_None; /* correct */
    return(Py_None); /* incorrect */

  • 函数和宏的调用风格:foo(a, b, c)–在左括号之前没有空格,在括号中没有空格,在逗号前没有空格,在每个逗号后一个空格。

  • 总是在赋值符号,布尔值和比较符两边加上括号,在表达式中使用了很多操作符,在最外面的(最低优先级)的两边加上空格。

  • 长行换行:如果可以,在最外层表达式的逗号后折行。总是保持缩进

    1
    2
    3
    PyErr_Format(PyExc_TypeError,
    "cannot create '%.100s' instances",
    type->tp_name);
  • 当你在长表达式的二元操作符处换行,这个操作符跟在前一行的行末,例如:

    1
    2
    3
    4
    if (type->tp_dictoffset != 0 && base->tp_dictoffset == 0 &&
    type->tp_dictoffset == b_size &&
    (size_t)t_size == b_size + sizeof(PyObject *))
    return 0; /* "Forgive" adding a __dict__ only */
  • 在函数、结构定义、函数的主要片段的前后留空行。

  • 注释放在它们描述的函数之前。

  • 所有的函数和全局变量申明为static,除非他们是发布的接口的一部分

  • 对于外部函数和变量,我们总是在“Include”文件夹中合适的头文件中声明,使用PyAPI_FUNC()宏,如:

    PyAPI_FUNC(PyObject *) PyObject_Repr(PyObject *);

命名习惯

  • 公共函数使用Py前缀;永远不要给静态函数(使用Py前缀)。Py_前缀给全局服务例程如Py_FatalError;特定的服务组(例如特定对象类型API)使用长前缀例如字符串使用PyString_。

  • 公共函数和变量使用混合大小写以及下划线,例如这些:PyObject_GetAttrPy_BuildValuePyExc_TypeError

  • 偶尔的一个“内部”函数需要对加载器可见,对此我们使用_Py前缀,例如:_PyObject_Dump

  • 宏应该是混合大小写的前缀跟着大写,例如:PyString_AS_STRINGPy_PRINT_RAW

文档字符串

  • 对文档字符串使用PyDoc_STR()或者PyDoc_STRVAR()宏以获得无文档字符串地编译Python(./configure --without-doc-strings)。

    对于需要支持早于2.3版本Python的C代码,你可以在引入Python.h后包含如下:

    1
    2
    3
    4
    5
    #ifndef PyDoc_STR
    #define PyDoc_VAR(name) static char name[]
    #define PyDoc_STR(str) (str)
    #define PyDoc_STRVAR(name, str) PyDoc_VAR(name) = PyDoc_STR(str)
    #endif
  • 函数文档字符串的第一行应是一个“签名行”–给出一个参数和返回值简短的概要,例如:

    1
    2
    3
    PyDoc_STRVAR(myfunction__doc__,
    "myfunction(name, value) -> bool\n\n\
    Determine whether name and value make a valid pair.");

    总是包含一个空行在签名行和描述的文本之间。

    如果函数的返回值总是为None(因为没有有意义的返回值),不要写返回类型的提示。

  • 当写多行文档字符串时,确保总是像上面例子中使用反斜杠继续,或者字符串字面相连:

    1
    2
    3
    PyDoc_STRVAR(myfunction__doc__,
    "myfunction(name, value) -> bool\n\n"
    "Determine whether name and value make a valid pair.");

    尽管一些C编译器接受没有上述两种规范的字符串:

    1
    2
    3
    4
    /* BAD -- don't do this! */
    PyDoc_STRVAR(myfunction__doc__,
    "myfunction(name, value) -> bool\n\n
    Determine whether name and value make a valid pair.");

    不要这么做;已知MSVC编译器会抱怨这个。

参考

[1] PEP 8 , “Style Guide for Python Code”, van Rossum, Warsaw ( http://www.python.org/dev/peps/pep-0008 )

版权信息

此文档已放在公共领域。

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