PEP 7 – C 代码风格指南
- 作者:
- Guido van Rossum <guido at python.org>,Barry Warsaw <barry at python.org>
- 状态:
- 活跃
- 类型:
- 流程
- 创建:
- 2001年7月5日
- 修订历史:
引言
本文档提供了 Python C 实现的 C 代码的编码约定。请参阅配套的信息性 PEP,其中描述了Python 代码的风格指南。
注意,规则是为了被打破而存在的。打破特定规则的两个充分理由是
- 当应用规则会使代码可读性降低时,即使对于习惯阅读遵循规则的代码的人也是如此。
- 为了与周围也违反该规则的代码保持一致(可能是出于历史原因)——尽管这也是清理他人代码混乱的机会(以真正的 XP 风格)。
C 方言
- Python 3.11 及更高版本使用 C11,不使用可选特性。公共 C API 应与 C++ 兼容。
- Python 3.6 到 3.10 使用 C89,并包含一些选定的 C99 特性
- 在
<stdint.h>
和<inttypes.h>
中使用标准整数类型。我们需要固定宽度的整数类型。 static inline
函数- 指定初始化器(对于类型声明尤其有用)
- 混合声明
- 布尔值
- C++ 风格的行注释
- 在
- 3.6 之前的 Python 版本使用 ANSI/ISO 标准 C(1989 年版本的标准)。这意味着(除其他事项外)所有声明必须位于代码块的顶部(不一定是函数的顶部)。
- 不要使用编译器特定的扩展,例如 GCC 或 MSVC 的扩展(例如,不要在没有尾部反斜杠的情况下编写多行字符串)。
- 所有函数声明和定义都必须使用完整的原型(即指定所有参数的类型)。
- 主要编译器(gcc、VC++、其他一些编译器)没有编译器警告。
- 在新的代码中,应优先使用
static inline
函数而不是宏。
代码布局
- 使用 4 个空格缩进,完全不使用制表符。
- 每行长度不应超过 79 个字符。如果此规则和上一条规则加在一起没有给你足够的编码空间,那么你的代码过于复杂——考虑使用子程序。
- 任何一行都不应该以空格结尾。如果你认为需要重要的尾随空格,请再三考虑——某些人的编辑器可能会将其作为例行公事删除。
- 函数定义风格:函数名位于第 1 列,最外层花括号位于第 1 列,局部变量声明后有空行。
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; }
- 代码结构:在
if
、for
等关键字和后面的左括号之间留一个空格;括号内没有空格;在所有地方都需要花括号,即使在 C 允许省略它们的地方,但不要将其添加到你没有修改的其他代码中。所有新的 C 代码都需要花括号。花括号应按如下所示格式化if (mro != NULL) { ... } else { ... }
- return 语句不应包含冗余括号
return(albatross); /* incorrect */
改为
return albatross; /* correct */
- 函数和宏调用风格:
foo(a, b, c)
——左括号前没有空格,括号内没有空格,逗号前没有空格,每个逗号后有一个空格。 - 始终在赋值、布尔和比较运算符周围添加空格。在使用大量运算符的表达式中,在最外层(最低优先级)运算符周围添加空格。
- 换行:如果可以,在最外层参数列表中的逗号后换行。始终适当地缩进续行,例如
PyErr_Format(PyExc_TypeError, "cannot create '%.100s' instances", type->tp_name);
- 当你在二元运算符处换行时,花括号应按如下所示格式化
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 */ }
将运算符放在行尾是可以的,特别是为了与周围的代码保持一致。(有关更详细的讨论,请参阅PEP 8。)
- 在多行宏中垂直对齐续行字符。
- 旨在用作语句的宏应使用
do { ... } while (0)
宏习语,不带最终分号。示例#define ADD_INT_MACRO(MOD, INT) \ do { \ if (PyModule_AddIntConstant((MOD), (#INT), (INT)) < 0) { \ goto error; \ } \ } while (0) // To be used like a statement with a semicolon: ADD_INT_MACRO(m, SOME_CONSTANT);
- 在使用后
#undef
文件局部宏。 - 在函数、结构定义以及函数内部的主要部分周围放置空行。
- 注释放在它们描述的代码之前。
- 所有函数和全局变量都应声明为静态,除非它们是要成为发布接口的一部分。
- 对于外部函数和变量,我们始终在“Include”目录中的相应头文件中有一个声明,该声明使用
PyAPI_FUNC()
宏和PyAPI_DATA()
宏,如下所示PyAPI_FUNC(PyObject *) PyObject_Repr(PyObject *); PyAPI_DATA(PyTypeObject) PySuper_Type;
命名约定
- 对于公共函数使用
Py
前缀;对于静态函数则从不使用。Py_
前缀保留用于全局服务例程,例如Py_FatalError
;特定组的例程(例如,特定对象类型的 API)使用更长的前缀,例如字符串函数的PyString_
。 - 公共函数和变量使用混合大小写并带下划线,例如:
PyObject_GetAttr
、Py_BuildValue
、PyExc_TypeError
。 - 有时“内部”函数必须对加载器可见;我们为此使用
_Py
前缀,例如:_PyObject_Dump
。 - 宏应具有混合大小写前缀,然后使用大写,例如:
PyString_AS_STRING
、Py_PRINT_RAW
。 - 宏参数应使用
ALL_CAPS
样式,以便于将其与 C 变量和结构成员区分开来。
文档字符串
- 对于文档字符串,使用
PyDoc_STR()
或PyDoc_STRVAR()
宏,以支持在没有文档字符串的情况下构建 Python(./configure --without-doc-strings
)。 - 每个函数文档字符串的第一行应为“签名行”,简要概述参数和返回值。例如
PyDoc_STRVAR(myfunction__doc__, "myfunction(name, value) -> bool\n\n\ Determine whether name and value make a valid pair.");
始终在签名行和描述文本之间包含一个空行。
如果函数的返回值始终为
None
(因为没有有意义的返回值),则不包含返回值类型的指示。 - 编写多行文档字符串时,请务必始终使用反斜杠续行,如上例所示,或使用字符串文字连接
PyDoc_STRVAR(myfunction__doc__, "myfunction(name, value) -> bool\n\n" "Determine whether name and value make a valid pair.");
尽管一些 C 编译器接受没有两者之一的字符串文字
/* 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 编译器会对此提出抱怨。
版权
本文档已置于公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0007.rst
上次修改时间:2024年9月9日14:02:27 GMT