原文
https://legacy.python.org/dev/peps/pep-0008/
中文翻译版本
https://blog.csdn.net/ratsniper/article/details/78954852
PEP8代码规范主要分为四个部分
- 代码布局
- 空格的使用
- 注释规范
- 命名规范
1.代码布局
1.1 Indentation 缩进
每一级缩进建议使用4个空格长度,并保持所有缩进都使用该标准,四空格的要求缩进对于挂行缩进是可选的
缩进方式的选择
缩进方式首选空格。
当然,也可以使用制表符(Tab),但是制表符只能和同样使用制表符缩进的代码保持一致。
也就是说:
缩进不可以空格和制表符混用
缩进不可以空格和制表符混用
缩进不可以空格和制表符混用
换行与缩进规范
——续行缩进应与包裹元素对齐
- 圆括号、方括号、花括号内的隐式行连接垂直对齐
与左括号对齐
foo = long_function_name(var_one, var_two,
var_three, var_four)
注意:没有使用垂直对齐的时候,禁止把参数放在第一行!
- 挂行对齐(第一行不应该有参数)
挂行缩进应该再换一行
foo = long_function_name(
var_one, var_two,
var_three, var_four)
#其他行
print(var_one)
注意:挂行缩进部分缩进长度,应与其他行进行区分
——条件语句缩进
语句的条件部分长到需要换行写的时候,注意可以在两个字符关键字的连接处(比如if),增加一个空格,再增加一个左括号来创造一个4空格缩进的多行条件。
区分 if 的条件代码和内嵌代码。可使用的选项包括但不限于下面几种情况:
1. 没有额外的缩进
if (this_is_one_thing and
that_is_another_thing):
do_something()
2. 增加一个注释,通过注释分区条件代码和内嵌代码
if (this_is_one_thing and
that_is_another_thing):
# Since both conditions are true, we can frobnicate.
do_something()
3. 在条件判断的语句添加额外的缩进
if (this_is_one_thing
and that_is_another_thing):
do_something()
——括号结构的缩进
- 多行结构中的大括号/中括号/小括号,的右括号可以与内容对齐单独起一行作为最后一行的第一个字符
my_list = [
1, 2, 3,
4, 5, 6,
]
result = some_function_that_takes_arguments(
'a', 'b', 'c',
'd', 'e', 'f',
)
- 也可以与多行结构的第一行第一个字符对齐
my_list = [
1, 2, 3,
4, 5, 6,
]
result = some_function_that_takes_arguments(
'a', 'b', 'c',
'd', 'e', 'f',
)
——每行多长必须换行缩进?
所有行限制的最大字符数为79。
没有结构化限制的大块文本(文档字符或者注释),每行的最大字符数限制在72。
——较长的代码行怎样换行缩进
- 括号
在小括号,中括号以及大括号中的隐式续行方式。通过小括号内表达式的换行方式将长串折成多行。这种方式应该优先使用,而不是使用反斜杠续行。 - 反斜杠
反斜杠有时依然很有用。比如,比较长的,多个with状态语句,不能使用隐式续行,所以反斜杠是可以接受的:
with open('/path/to/some/file/you/want/to/read') as file_1, \
open('/path/to/some/file/being/written', 'w') as file_2:
file_2.write(file_1.read())
——二元运算符的换行与缩进
在二元运算符之前开始换行并缩进
不推荐: 操作符离操作数太远
income = (gross_wages +
taxable_interest +
(dividends - qualified_dividends) -
ira_deduction -
student_loan_interest)
推荐:运算符和操作数很容易进行匹配
income = (gross_wages
+ taxable_interest
+ (dividends - qualified_dividends)
- ira_deduction
- student_loan_interest)
1.2 Blank Lines空行
- 顶层函数和类的定义,前后用两个空行隔开。
- 类里的方法定义之间用一个空行隔开。
谨慎使用以下规则:
相关的功能组可以用额外的空行隔开
一堆相关的单行代码之间的空白行可以省略
在函数中使用空行来区分逻辑段
Python接受control-L(即^L)换页符作为空格;许多工具把这些字符当作页面分隔符,所以你可以在文件中使用它们来分隔相关段落。请注意,一些编辑器和基于Web的代码阅读器可能无法识别control-L为换页,将在其位置显示另一个字形。
1.3 Source File Encoding文件编码
Python核心发布版本中的代码总是以UTF-8格式编码(或者在Python2中用ASCII编码)。
使用ASCII(在Python2中)或UTF-8(在Python3中)编码的文件不应具有编码声明。
Python标准库中的所有标识符必须使用ASCII标识符,并在可行的情况下使用英语单词(在许多情况下,缩写和技术术语是非英语的)。
此外,字符串文字和注释也必须是ASCII。
如果包含非ASCII字符,使用\x,\u,\U , 或者 \N 进行转义来包含非ASCII字符。
1.4 Imports 导入
——导入的格式
- 导入的内容位置不明确
推荐: import os
import sys
不推荐: import sys, os
- 导入的内容在位置明确
from subprocess import Popen, PIPE
——导入顺序
- 导入总是位于文件的顶部,在模块注释和文档字符串之后,在模块的全局变量与常量之前。
导入应该按照以下顺序分组:
- 标准库导入
- 相关第三方库导入
- 本地应用/库特定导入
你应该在每一组导入之间加入空行。
——导入路径
导入路径分为三种:1.纯绝对路径 2.显式地指定相对路径3.隐式相对路径
推荐使用绝对路径导入
使用绝对路径会明确引入内容的真实路径,更加可读并且性能更好(至少能提供更好的错误信息):
import mypkg.sibling
from mypkg import sibling
from mypkg.sibling import example
当绝对路径的引入层级多,结构复杂时,显式地指定相对引入路径是允许的
from . import sibling
from .sibling import example
不建议使用隐式相对路径引入,例如以下:
import xxx from ../../../include/img/icon/xxx
——导入包中的类
当从一个包含类的模块中导入类的时候,常这样进行:
from myclass import MyClass
from foo.bar.yourclass import YourClass
如果大小写会出现冲突,可以这样写:
import myclass
import foo.bar.yourclass
在使用的时候自行加上类的使用:
myclass.MyClass
foo.bar.yourclass.YourClass
——导入时对通配符*的使用
应该避免对通配符的使用*
因为不明确引入的内容是否会和当前文档中的内容名称产生冲突,会使得读取接口和许多自动化工具之间产生混淆。
对于通配符的导入,有一个防御性的做法,即将内部接口重新发布为公共API的一部分(例如,用可选加速器模块的定义覆盖纯Python实现的接口,以及重写那些事先不知道的定义)。
当以这种方式重新发布名称时,以下关于公共和内部接口的准则仍然适用。
1.5 Module level dunder names 模块级的“呆”名
像all , __author__ , __version__ 等这样的模块级“呆名“(也就是名字里有两个前缀下划线和两个后缀下划线)
应该放在文档字符串的后面,以及除from __future__ 之外的import表达式前面
"""This is the example module.
This module does stuff.
"""
from __future__ import barry_as_FLUFL
__all__ = ['a', 'b', 'c']
__version__ = '0.1'
__author__ = 'Cardinal Biggles'
import os
import sys
1.6字符串的引号使用
- 单引号与双引号的选择和使用
在Python中,单引号和双引号字符串效果是相同的,可以主要使用任意一种不做要求
当一个字符串中包含单引号或双引号的字符时,在外层包裹不同的符号,来避免使用反斜杠,从而提高可读性。
- 三引号
三引号字符串为了进行与```符号的区分,选择双引号字符"""进行使用
1.7复合语句
复合语句通常是不允许的,这样会降低可读性,请将代码拆分为多行设置
2.空格的使用
——括号附近的空格
- 不要在小括号、中括号、大括号之后或之前添加空格
Yes: spam(ham[1], {eggs: 2})
No: spam( ham[ 1 ], { eggs: 2 } )
——逗号、分号、冒号附近
- 不要在逗号、冒号、分号之前添加空格,在其之后添加一个即可
Yes: if x == 4: print x, y; x, y = y, x
No: if x == 4 : print x , y ; x , y = y , x
- 当冒号作用在切片操作中时,两边应该有等量的空格数。例外:当一个切片参数被省略时,和它相邻的冒号之间需要省略
YES:
ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:]
ham[lower:upper], ham[lower:upper:], ham[lower::step]
ham[lower+offset : upper+offset]
ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
ham[lower + offset : upper + offset]
NO:
ham[lower + offset:upper + offset]
ham[1: 9], ham[1 :9], ham[1:9 :3]
ham[lower : : upper]
ham[ : upper]
——二元运算符
- 记得总是在二元运算符两边添加一个空格:赋值(=),增量赋值(+=,-=),比较(==,<,>,!=,<>,<=,>=,in,not,in,is,is not),布尔(and, or, not)。
- 如果语句中有不同优先级的运算符,请在最低级运算符符周围添加一个空格(例如有乘除加减,加减就是最低级运算符)
- 如果语句中有不同优先级的运算符,优先运算的运算符不用添加空格
YES:
i = i + 1
submitted += 1
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)
NO:
i=i+1
submitted +=1
x = x * 2 - 1
hypot2 = x * x + y * y
c = (a + b) * (a - b)
——赋值
- 在制定关键字参数或者默认参数值的时候,不要在=附近加上空格。
YES:
def complex(real, imag=0.0):
return magic(r=real, i=imag)
NO:
def complex(real, imag = 0.0):
return magic(r = real, i = imag)
- 当给有类型备注的参数赋值的时候,在=两边添加空格(仅针对那种有类型备注和默认值的参数)。
YES:
def munge(sep: AnyStr = None): ...
def munge(input: AnyStr, sep: AnyStr = None, limit=1000): ...
NO:
def munge(input: AnyStr=None): ...
def munge(input: AnyStr, limit = 1000): ...
——对齐
- 不要使用添加多余的空格来试图结构对齐
YES:
x = 1
y = 2
long_variable = 3
NO:
x = 1
y = 2
long_variable = 3
——语句尾部
- 避免在尾部添加空格。因为尾部的空格通常都看不见,会产生混乱。比如,一个反斜杠后面跟一个空格的换行符,不算续行标记。
3.Comments 注释
- 注释应该是完成的句子,给阅读者最低的理解成本
- 注释结束的时候应添加两个空格
3.1Block Comments 块注释
- 块注释通常适用于跟随它们的某些(或全部)代码,并缩进到与代码相同的级别。
- 块注释的每一行开头使用一个#和一个空格(除非块注释内部缩进文本)。
- 块注释内部的段落通过只有一个#的空行分隔。
# line01
# line02
#
# line03
3.2Inline Comments 行内注释
- 行内注释是与代码语句同行的注释。
- 行内注释和代码至少要有两个空格分隔。注释由#和一个空格开始。
x = x + 1 # Compensate for border
3.3Documentation Strings 文档字符串
- 要为所有的公共模块,函数,类以及方法编写文档说明。非公共的方法没有必要,但是应该有一个描述方法具体作用的注释。这个注释应该在def那一行之后。
- 特别需要注意的是,多行文档说明使用的结尾三引号应该自成一行
"""Return a foobang
Optional plotz says to frobnicate the bizbaz first.
"""
- 对于单行的文档说明,尾部的三引号应该和文档在同一行。
"""Return a foot """
4.Naming Conventions 命名规范
4.1命名原则
那些暴露给用户的API接口的命名,应该遵循反映使用场景而不是实现的原则。
4.2命名风格
以下是常见的命名方式
- b(单个小写字母)
- B(单个大写字母)
- show(统一小写字母)
- show_title(使用下划线分隔小写字母)
- SHOW(统一大写字母)
- SHOW_TITLE(使用下划线分隔大写字母)
- ShowTitle(大驼峰命名法)
- showTitle(小驼峰命名法)
- Show_Title(各种混用)
另外,下面这种用前缀或结尾下划线的特殊格式是被认可的(通常和一些约定相结合):
- _show(单下划线开头)。受保护的对象成员,在类/对象外部不可访问
- show_(单下划线结尾)。这是避免和Python内部关键词冲突的一种约定(例:class_=’ClassName’)
- __show(双下划綫开头)。当这样命名一个类的属性时,调用它的时候名字会做矫正:(在类FooBar中,__boo变成了_FooBar__boo)
- __show__(双下划綫开始,双下划綫结尾)。私有的对象成员,在类/对象外部和子类中不可访问
4.3命名规范
——应避免的名字
- 在命名的时候永远不要使用字母‘l’(小写的L),‘O’(大写的O),或者‘I’(大写的I)作为单字符变量名。
在有些字体里,这些字符无法和数字0和1区分,如果想用‘l’,用‘L’代替。
——Package and Module Names包名和模块名
- 模块:模块应该用简短全小写的名字,如果为了提升可读性,下划线也是可以用的。
- 包名:包名也应该使用简短全小写的名字,但不建议用下划线。
- 当使用C或者C++编写了一个依赖于提供高级(更面向对象)接口的Python模块的扩展模块,这个C/C++模块需要一个下划线前缀(例如:_socket)
——Class Names类名
- 类名一般使用首字母大写的约定。
- 在接口被文档化并且主要被用于调用的情况下,可以使用函数的命名风格代替。
- 注意,对于内置的变量命名有一个单独的约定:大部分内置变量是单个单词(或者两个单词连接在一起),首字母大写的命名法只用于异常名或者内部的常量。
——Exception Names 异常名
- 因为异常一般都是类,所有类的命名方法在这里也适用。然而,你需要在异常名后面加上“Error”后缀(如果异常确实是一个错误)。
——Global Variable Names 全局变量名
- (我们希望这一类变量只在模块内部使用。)约定和函数命名规则一样。
- 注意:通过 from M import * 导入的模块应该使用all机制去防止内部的接口对外暴露,或者使用在全局变量前加下划线的方式(表明这些全局变量是模块内非公有)。
——Function Names 函数名
- 函数名应该小写,如果想提高可读性可以用下划线分隔。
——Function and method arguments 函数和方法参数
- 始终要将 self 作为实例方法的的第一个参数。
- 始终要将 cls 作为类静态方法的第一个参数。
- 如果函数的参数名和已有的关键词冲突,在最后加单一下划线比缩写或随意拼写更好。因此 class_ 比 clss 更好。(也许最好用同义词来避免这种冲突)
——Method Names and Instance Variables 方法名和实例变量
应遵循这样的函数命名规则:
- 使用下划线分隔小写单词以提高可读性。
- 在非共有方法和实例变量前使用单下划线。
- 通过双下划线前缀触发Python的命名转换规则来避免和子类的命名冲突。
——Constants 常量
- 定义在模块级
- 通过下划线分隔的全大写字母命名。例如: MAX_OVERFLOW 和 TOTAL
5.其他建议
——和None进行比较
- 和None进行比较的时候,应该始终使用is 或者 is not ,永远不要使用等号运算符
- 使用is not 运算符,而不是 not ... is 。虽然功能完全相同,但前者更易于阅读
YES:
if foo is not None:
NO:
if not foo is None:
——富比较
- 当使用富比较(rich comparisons,一种复杂的对象间比较的新机制,允许返回值不为-1,0,1)实现排序操作的时候,最好实现全部的六个操作符(__eq__, __ne__, __lt__, __gt__, __ge__)而不是依靠其他的代码去实现特定的比较。
YES:
def f(x): return 2*x
NO:
f = lambda x: 2*x
——True、False比较
- 不要用 == 和True、False进行比较
正确: if greeting:
糟糕: if greeting == True:
更糟: if greeting is True:
——对象类型的比较
- 对象类型的比较应该用isinstance()而不是直接比较type。
正确: if isinstance(obj, int):
糟糕: if type(obj) is type(1):
——对空序列的判断
- 对于序列来说(strings,lists,tuples),可以使用空序列为false的情况。
正确: if not seq:
if seq:
糟糕: if len(seq):
if not len(seq):
——字符串切割检索
- 使用 ”.startswith() 和 ”.endswith() 代替通过字符串切割的方法去检查前缀和后缀。
startswith()和endswith()更干净,出错几率更小。
推荐: if foo.startswith('bar'):
糟糕: if foo[:3] == 'bar':
——try/except 语句
- 当捕获到异常时,如果可以的话写上具体的异常名,而不是只用一个except: 块。
try:
import platform_specific_module
except ImportError:
platform_specific_module = None
- 另外,对于所有的 try/except 语句块,在try语句中只填充必要的代码,这样能避免掩盖掉bug。
YES:
try:
value = collection[key]
except KeyError:
return key_not_found(key)
else:
return handle_value(value)
NO:
try:
# Too broad!
return handle_value(collection[key])
except KeyError:
# Will also catch KeyError raised by handle_value()
return key_not_found(key)
- 当代码片段局部使用了某个资源的时候,使用with 表达式来确保这个资源使用完后被清理干净。用try/finally也可以。