Python 代码规范

编码是将信息从一种新式或格式转变为另一种形式的过程,在计算机硬件中,编码就是利用代码来表示各组数据资料,使计算机能够读懂这行代码并对信息进行分析和处理。

统一代码规范,就是统一代码的组织方式,让团队以最优的、一致的方式及组织代码,从而减少额外的阅读代码的负担。

1 编码格式

1.1 文件头声明(已废弃)

在 Python 的编程过程中,之前需要注意以下两点:

  • 如无特殊情况,文件一律使用 UTF-8 编码
  • 如无特殊情况,文件头部必须加入 #-*-coding:utf-8-*- 标识

但在 Python 3 中,默认是以“utf-8”对代码内容进行读取和保存的,所以这个头部#-*-coding:utf-8-*- 标识已不重要的

1.2 缩进

同一使用4个空格进行缩进,就是一个Tab

1.3 行宽

每行代码尽量不要超过80个字符(特殊情况下可超过,但最长不得超过120)

如果代码过长,可能存在着以下三种问题:

  • 这在查看 side-by-side 的 diff 时很有帮助
  • 方便在控制台下查看代码
  • 太长可能是设计有缺陷

1.4 引号

编码过程中,如果需要计算机直接输出你想要的信息,我们就要用到引号,自然语言用双引号,机器标示用单引号,代码中多数使用单引号,举个例子:

  • 自然语言使用双引号 "..." , 例如错误信息;直接采用这种方式 "错误信息"
  • 机器标识使用单引号 '...' 例如 dict 里的 key
  • 正则表达式使用原生的双引号 r"..."
  • 文档字符串 (docstring)使用三个双引号 """......"""

1.5 空格

在二元运算符两边各空一格[=,-,+=,==,>,in,is not, and]:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 正确的写法
i = i + 1
submitted += 2
y = y * 2 - 1
hypot2 = x * x + y * y
c = (a + b) * (a - b)

# 不推荐的写法
i=i+1
submitted +=2
y = y*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)

函数的参数列表中,,之后要有空格

1
2
3
4
5
6
7
# 正确的写法
def complex(real, imag):
pass

# 不推荐的写法
def complex(real,imag):
pass

函数的参数列表中,默认值等号两边不要添加空格

1
2
3
4
5
6
7
# 正确的写法
def complex(real, imag=0.0):
pass

# 不推荐的写法
def complex(real, imag = 0.0):
pass

左括号之后,右括号之前不要加多余的空格

1
2
3
4
5
# 正确的写法
spam(ham[1], {eggs: 2})

# 不推荐的写法
spam( ham[1], { eggs : 2 } )

字典对象的左括号之前不要多余的空格

1
2
3
4
5
# 正确的写法
dict['key'] = list[index]

# 不推荐的写法
dict ['key'] = list [index]

不要为对齐赋值语句而使用的额外空格

1
2
3
4
5
6
7
8
9
# 正确的写法
x = 1
y = 2
long_variable = 3

# 不推荐的写法
x = 1
y = 2
long_variable = 3

1.6 换行

1.6.1 空行

模块级函数和类定义之间空两行;
类成员函数之间空一行;

空行示例
1
2
3
4
5
6
7
8
9
10
11
class A:

def __init__(self):
pass

def hello(self):
pass


def main():
pass
  • 可以使用多个空行分隔多组相关的函数
  • 函数中可以使用空行分隔出逻辑相关的代码

1.6.2 条件语句换行

if/for/while 一定要换行:

1
2
3
4
5
6
# 正确的写法
if foo == 'blah':
do_blah_thing()

# 不推荐的写法
if foo == 'blah': do_blash_thing()

禁止复合语句,即一行中包含多个语句:

1
2
3
4
5
6
7
# 正确的写法
do_first()
do_second()
do_third()

# 不推荐的写法
do_first();do_second();do_third();

1.6.3 反斜杠换行

使用反斜杠\换行,二元运算符+.等应出现在行末;长字符串也可以用此法换行。

1
2
3
4
5
6
7
8
9
# 链式调用方法过长换行
session.query(MyTable).\
filter_by(id=1).\
one()

# 长字符串换行
print 'Hello, '\
'%s %s!' %\
('Harry', 'Potter')

1.7 括号

1.7.1 尽量少使用无谓的括号

1
2
3
4
5
6
7
8
9
10
11
12
# 推荐
if foo:
bar()
while x:
x = bar()

# 不推荐
if (x):
bar()
if not(x):
bar()
return (foo)

2 命名

2.1 基础

  • 模块名写法: module_name ;
  • 包名写法: package_name ;
  • 类名: ClassName ;
  • 方法名: method_name ;
  • 异常名: ExceptionName ;
  • 函数名: function_name ;
  • 全局常量名: GLOBAL_CONSTANT_NAME ;
  • 全局变量名: global_var_name ;
  • 实例名: instance_var_name ;
  • 函数参数名: function_parameter_name ;
  • 局部变量名: local_var_name .

函数名, 变量名和文件名应该是描述性的, 尽量避免缩写,特 别要避免使用非项目人员不清楚难以理解的缩写, 不要通过删除单词中的字母来进行缩写。始终使用 .py 作为文件后缀名,不要用破折号.

3 注释

3.1 docstring

Python有一种独一无二的的注释方式: 使用文档字符串. 文档字符串是包, 模块, 类或函数里的第一个语句. 这些字符串可以通过对象的 __doc__ 成员被自动提取, 并且被pydoc所用.

docstring 文档字符串是一个重要的工具,用于解释文档程序,帮助你的程序文档更加易懂。

其最需要注意的规范有两点:

所有的公共模块、函数、类、方法,都应该写 docstring 。私有方法不一定需要,但应该在 def 后提供一个块注释来说明。
docstring 的结束"""应该独占一行,除非此 docstring 只有一行。

1
2
3
4
5
6
7
del function():
'''say something here!
'''
pass
print(function.__doc__) # 调用doc

# 输出: say something here!

文档注释示例:

1
2
3
4
5
6
7
8
9
10
11
12
"""模块概述.

上面空一行,这里是模块具体的描述.

Typical usage example:

foo = ClassFoo()
bar = foo.FunctionBar()
"""

__author__ = 'luochenxun'
__version__ = '1.0.0

3.2 函数文档

几部分:

  1. 函数概述;
  2. 函数具体描述(函数的作用等,但不要包含实现细节);
  3. Args: 参数
  4. Returns: 描述返回值的类型和语义. 如果函数返回None, 这一部分可以省略.
  5. Raise: 与接口有关的所有异常
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
def fetch_smalltable_rows(table_handle: smalltable.Table,
keys: Sequence[Union[bytes, str]],
require_all_keys: bool = False,
) -> Mapping[bytes, Tuple[str]]:
"""函数概述.

Retrieves rows pertaining to the given keys from the Table instance
represented by table_handle. String keys will be UTF-8 encoded.

Args:
table_handle:
An open smalltable.Table instance.
keys:
A sequence of strings representing the key of each table
row to fetch. String keys will be UTF-8 encoded.
require_all_keys:
Optional; If require_all_keys is True only
rows with values set for all keys will be returned.

Returns:
A dict mapping keys to the corresponding table row data
fetched. Each row is represented as a tuple of strings. For
example:

{b'Serak': ('Rigel VII', 'Preparer'),
b'Zim': ('Irk', 'Invader'),
b'Lrrr': ('Omicron Persei 8', 'Emperor')}

Returned keys are always bytes. If a key from the keys argument is
missing from the dictionary, then that row was not found in the
table (and require_all_keys must have been False).

Raises:
IOError: An error occurred accessing the smalltable.
"""

3.3 类文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class SampleClass(object):
"""Summary of class here.

Longer class information....
Longer class information....

Attributes:
likes_spam: A boolean indicating if we like SPAM or not.
eggs: An integer count of the eggs we have laid.
"""

def __init__(self, likes_spam=False):
"""Inits SampleClass with blah."""
self.likes_spam = likes_spam
self.eggs = 0

def public_method(self):
"""Performs operation blah."""

4 字符串

4.1 长字符串

尽量少使用反斜杠连接行,使用圆括号的隐式连接:

长字符串示例
1
2
x = ('This will build a very long long '
'long long long long long long string')

4.2 格式化字符串

即使参数都是字符串, 使用%操作符或者格式化方法格式化字符串. 不过也不能一概而论, 你需要在+%之间好好判定.

1
2
3
4
5
6
7
8
9
10
11
12
# 推荐
x = a + b
x = '%s, %s!' % (imperative, expletive)
x = '{}, {}!'.format(imperative, expletive)
x = 'name: %s; score: %d' % (name, n)
x = 'name: {}; score: {}'.format(name, n)

# 不推荐
x = '%s%s' % (a, b) # 使用 + 更好
x = '{}{}'.format(a, b) # 使用 + 更好
x = imperative + ', ' + expletive + '!' # 这里较复杂的应该使用 % 格式化字符串
x = 'name: ' + name + '; score: ' + str(n)

上面不推荐的例子好好看一,如果没有特殊的字符串加入的话,使用 + 会更清晰。

4.3 TODO注释

TODO注释应该在所有开头处包含”TODO”字符串, 紧跟着是用括号括起来的你的名字, email地址或其它标识符. 然后是一个可选的冒号. 接着必须有一行注释, 解释要做什么

1
2
# TODO(kl@gmail.com): Use a "*" here for string repetition.
# TODO(Zeke) Change this to use relations.

5 常用语句

5.1 import语句

编写的正确方式,应该是分行书写。

import语句示例
1
2
3
4
5
6
7
8
9
# 正确的写法
import os
import sys

# 不正确的写法
import sys,os

# 正确的写法
from subprocess import Popen, PIPE

import语句应该放在文件头部,置于模块说明及docstring之后,于全局变量之前
import语句应该按照顺序排列,每组之间用一个空行分隔

import示例
1
2
3
4
5
6
7
8
9
10
# 系统库
import os
import sys

# 三方库
import msgpack
import zmq

# 项目私有库
import foo

导入其他模块的类定义时,可以使用相对导入

相对导入
1
from myclass import MyClass

5.2 main

在Python中, pydoc以及单元测试要求模块必须是可导入的. 你的代码应该在执行主程序前总是检查 if __name__ == '__main__' , 这样当模块被导入时主程序就不会被执行.

若使用 absl, 请使用 app.run :

1
2
3
4
5
6
7
8
9
from absl import app
# ...

def main(argv):
# process non-flag arguments
# ...

if __name__ == '__main__':
app.run(main)

否则,使用:

1
2
3
4
5
def main():
...

if __name__ == '__main__':
main()

6 参考文献

  1. 《Google - Python风格规范》