(1)文档仅适用于基于Python主要发行版本的标准库。
(2)文档中的约定应基于现实考虑灵活处理,根据自身需求和团队其他成员的阅读习惯做适当调整。
(3)若遵循文档中的规范使得代码可读性更差,或者因不符合个人习惯会造成代码错误,应适当采用。
(4)已完成的代码未很好的遵循文档规范,不建议重新修改。
每一级缩进使用4个空格。
续行应该与其包裹元素对齐,要么使用圆括号、方括号和花括号内的隐式行连接来垂直对齐,要么使用挂行缩进对齐。
当使用挂行缩进时,应该考虑到第一行不应该有参数,以及使用缩进以区分自己是续行。
推荐:
# 与左括号对齐(垂直对齐) foo = long_function_name(var_one, var_two, var_three, var_four) # 用更多的缩进来与其他行区分 def long_function_name( var_one, var_two, var_three, var_four): print(var_one) # 挂行缩进应该再换一行 foo = long_function_name( var_one, var_two, var_three, var_four)
不推荐:
# 没有使用垂直对齐时,禁止把参数放在第一行 foo = long_function_name(var_one, var_two, var_three, var_four) # 当缩进没有与其他行区分时,要增加缩进,避免逻辑上的混乱 def long_function_name( var_one, var_two, var_three, var_four): print(var_one)
如果两个字符的关键字(这里为if)加上一个空格和一个左括号可以组合成为一个4空格的缩进。可能会造成if语句内部代码和判断条件视觉上的冲突,一般有如下几种处理方式。
# 没有额外的缩进,无法区分函数头和代码块 if (this_is_one_thing and that_is_another_thing): do_something() # 增加一个注释,在能提供语法高亮的编辑器中可以有一些区分 if (this_is_one_thing and that_is_another_thing): # Since both conditions are true, we can frobnicate. do_something() # 在条件判断的语句添加额外的缩进 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' )
(1)首选的缩进方式应选择空格。
(2)制表符只能用于与同样使用制表符缩进的代码保持一致。
(3)Python3不允许混合使用空格和制表符。
(4)Python2开发的项目代码中应将混合使用空格和制表符的内容统一修改为空格。
(5)用命令行调用Python2项目代码时,建议加入-t
或者-tt
选项。加入-t
会在混合使用制表符和空格的位置发出警告,加入-tt
这些警告会变成错误,给予提示。
(1)所有行限制的最大字符数为79个字符。
(2)对于结构限制较少的长文本块(文档字符或者注释),行长度应限制为72个字符。
(3)限制所需的编辑器窗口宽度(控制行长度)可以使多个文件并排打开,并且在相邻列中显示两个版本的代码审阅工具时效果更好。
(4)如果代码主要由一个团队维护,且同意使用更长的行宽,则可以把长度从80增加到100(或99),但是注释和文档字符仍然应保持72个字符换行。
(5)Python标准库相对保守,需要将行宽限制在79个字符(文档字符和注释限制在72个字符)。
(6)较长行的代码首选的换行方式是在括号、方括号和大括号内使用Python的隐式换行,不应优先使用反斜杠来换行。
(7)多个with
(此外还有:long
、multiple
等)状态语句下句子较长,不能使用隐式换行,应使用反斜杠换行:
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)
(1)顶层函数和类的定义,前后应用两个空行隔开。
(2)类里的方法定义用一个空行隔开。
(3)相关的功能组可以用额外的空行隔开,相关的单行代码之间的空白行可以省略。
(4)应尽量避免在函数中使用空行来区分逻辑段。
(1)Python核心发布版本中的代码总是以UTF-8格式编码(或者在Python2中用ASCII编码)。
(2)标准库中非默认的编码(UTF-8和ASCII)应只用于测试。注释或文档字符串中需要出现含ASCII字符编码的内容时,应该使用\x,\u,\U , 或者 \N 进行转义。
(3)使用ASCII(在Python2中)或UTF-8(在Python3中)编码的文件不应具有编码声明(# -*- coding: UTF-8 –*-
)。
(4)Python3和更高版本标准库规定:所有标识符必须使用ASCII
标识符,并在可行的情况下使用英语单词。此外,字符串文字和注释也必须是ASCII
编码格式。唯一的例外是(a)测试非ASCII
特征的测试用例,以及(b)作者的名称。作者的名字如果不使用拉丁字母拼写,必须提供一个拉丁字母的音译。
(1)导入通常应逐行进行,例如:
推荐: import os import sys 不推荐: import sys, os
但是可以这样:
from subprocess import Popen, PIPE
(2)导入总是位于文件的顶部,在模块注释和文档字符串之后,在模块的全局变量与常量之前。
导入应该按照以下顺序分组:
① 标准库导入
②相关第三方库导入
③本地应用/库特定导入 且应该在每一组导入之间加入空行。
(3)为提高可读性,应使用绝对路径导入:
import mypkg.sibling from mypkg import sibling from mypkg.sibling import example
在绝对路径过于冗长时也可使用显式的相对路径进行导入:
from . import sibling from .sibling import example
(4)当从一个包含类的模块中导入类时,通常应该写成如下格式:
from myclass import MyClass from foo.bar.yourclass import YourClass
如果上述的写法导致名字的冲突,那么应改为如下方式:
import myclass import foo.bar.yourclass
然后使用“myclass.MyClass
”和“foo.bar.yourclass.YourClass
”。
(5)应极力避免使用通配符的方式导入(from import *)。
dunder
dunder
指的是像__all__
, __author__
, __version__
等这样的模块级名称。dunder
应该放在文档字符串的后面,以及除from __future__
之外的import表达式前面。Python要求将来在模块中的导入,必须出现在除文档字符串之外的其他代码之前。
"""This is the example module. This module does stuff. """ __all__ = ['a', 'b', 'c'] __version__ = '0.1' __author__ = 'Cardinal Biggles' from __future__ import barry_as_FLUFL import os import sys
在Python中,单引号和双引号字符串是相同的。当一个字符串中包含单引号或者双引号字符的时候,使用和最外层不同的符号来避免使用反斜杠,从而提高可读性。
(1)紧跟在小括号,中括号或者大括号后。
Yes: spam(ham[1], {eggs: 2}) No: spam( ham[ 1 ], { eggs: 2 } )
(2)紧贴在逗号、分号或者冒号之前。
Yes: if x == 4: print x, y; x, y = y, x No: if x == 4 : print x , y ; x , y = y , x
(3)冒号在切片中类似于二元运算符,在两边应该有相同数量的空格。例外情况:当一个切片参数被省略时,空格就被省略。
推荐写法:
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]
不推荐写法:
ham[lower + offset:upper + offset] ham[1: 9], ham[1 :9], ham[1:9 :3] ham[lower : : upper] ham[ : upper]
(4)紧贴在函数参数的左括号之前。
Yes: spam(1) No: spam (1)
(5)紧贴索引或者切片的左括号之前。
Yes: dct['key'] = lst[index] No: dct ['key'] = lst [index]
(6)为了和另一个赋值语句对齐,在赋值运算符附件加多个空格。
推荐:
x = 1 y = 2 long_variable = 3
不推荐:
x = 1 y = 2 long_variable = 3
(1)避免在尾部添加空格。因为尾部的空格通常都看不见,会产生混乱:比如,一个反斜杠后面跟一个空格的换行符,不算续行标记。
(2)总是在二元运算符两边加一个空格:赋值(=),增量赋值(+=,-=),比较(==,<,>,!=,<>,<=,>=,in,not,in,is,is not),布尔(and, or, not)。
(3)如果使用具有不同优先级的运算符,请考虑在具有最低优先级的运算符周围添加空格。但是,不要使用一个以上的空格,并且在二元运算符的两边使用相同数量的空格。
推荐:
i = i + 1 submitted += 1 x = x*2 - 1 hypot2 = x*x + y*y c = (a+b) * (a-b)
不推荐:
i=i+1 submitted +=1 x = x * 2 - 1 hypot2 = x * x + y * y c = (a + b) * (a - b)
(4)在制定关键字参数或者默认参数值的时候,不要在=附近加上空格。
推荐:
def complex(real, imag=0.0): return magic(r=real, i=imag)
不推荐:
def complex(real, imag = 0.0): return magic(r = real, i = imag)
(5)功能型注释应该使用冒号的一般性规则,并且在使用->
的时候要在两边加空格。
推荐:
def munge(input: AnyStr): ... def munge() -> AnyStr: ...
不推荐:
def munge(input:AnyStr): ... def munge()->PosInt: ...
(6)当给有类型备注的参数赋值的时候,在=两边添加空格(仅针对那种有类型备注和默认值的参数)。
推荐:
def munge(sep: AnyStr = None): ... def munge(input: AnyStr, sep: AnyStr = None, limit=1000): ...
不推荐:
def munge(input: AnyStr=None): ... def munge(input: AnyStr, limit = 1000): ...
(7)复合语句(同一行中的多个语句)通常是不允许以空格分隔的。
推荐:
if foo == 'blah': do_blah_thing() do_one() do_two() do_three()
不推荐:
if foo == 'blah': do_blah_thing() do_one(); do_two(); do_three()
(8)有多行语句块时不要将小的代码块和 if/for/while 放在同一行,同时也要避免代码行太长。
不推荐:
if foo == 'blah': do_blah_thing() for x in lst: total += x while t < 10: t = delay()
绝对要避免的:
if foo == 'blah': do_blah_thing() else: do_non_blah_thing() try: something() finally: cleanup() do_one(); do_two(); do_three(long, argument, list, like, this) if foo == 'blah': one(); two(); three()
(1)错误的注释不如没有注释!
(2)代码更改前,优先更新对应的注释!
(3)注释应该是完整的句子。如果注释是英文短语或句子,它的第一个单词应该大写,除非它是以小写字母开头的标识符。
(4)块注释一般由完整句子的一个或多个段落组成,并且每句话结束应有个句号。
(1)块注释通常适用于跟随它们的某些(或全部)代码,并缩进到与代码相同的级别。通常块注释的每一行开头使用一个#和一个空格。
(2)块注释内部的段落通过只有一个#的空行分隔。
行内注释是与代码语句同行的注释。行内注释和代码至少要有两个空格分隔。注释由#和一个空格开始。事实上,如果状态明显的话,行内注释一般是不必要的,
不需要的写法:
x = x + 1 # 增量x
需要的写法:
x = x + 1 # 边界补偿
(1)要为所有的公共模块,函数,类以及方法编写文档说明。
(2)多行文档说明使用的结尾三引号应该自成一行,例如:
"""Return a foobang Optional plotz says to frobnicate the bizbaz first. """
对于单行的文档说明,尾部的三引号应该和文档在同一行。
那些给用户的API接口的命名,应该遵循反映使用场景而不是实现的原则(例如‘剪切’功能)。
常见的命名方式:
b
(单个小写字母)
B
(单个大写字母)
lowercase
(小写字母)
lower_case_with_underscores
(使用下划线分隔的小写字母,写的最多)
UPPERCASE
(大写字母)
UPPER_CASE_WITH_UNDERSCORES
(使用下划线分隔的大写字母)
CapitalizedWords
(驼峰命名法) 注意:当在首字母大写的风格中用到缩写时,所有缩写的字母用大写,因此,HTTPServerError
比 HttpServerError
好。
mixedCase
(不同于首字母大写,第一个单词的首字母小写)
Capitalized_Words_With_Underscores
(每个单词大写开头,且使用下划线,最不美观,应避免!)
一些使用前缀或结尾下划线的特殊格式是被认可的(通常和一些约定相结合):
_single_leading_underscore
:(单下划线开头)比如 from M import * 是不会导入以下划线开始的对象的。
single_trailing_underscore_
:(单下划线结尾)这是避免和Python内部关键词冲突的一种约定,比如:Tkinter.Toplevel
(master
, class_
=’ClassName
’)
__double_leading_underscore
:(双下划线开头)当这样命名一个类的属性时,调用它的时候名字会做矫正(在类FooBar
中,__boo
变成了_FooBar__boo
;)。
__double_leading_and_trailing_underscore__
:(双下划线开头,双下划线结尾)“magic
”对象或者存在于用户控制的命名空间内的属性,例如:__init__
,__import__
或者__file__
。除了作为文档之外,要避免这样的名。
(1)应避免的名字
永远不要使用字母‘l’(小写的L),‘O’(大写的O),或者‘I’(大写的i)作为单字符变量名。因为在有些字体里,这些字符无法和数字0和1区分。
(2)包名和模块名
模块应该用简短全小写的名字,如果为了提升可读性,可以使用下划线。Python包名也应该使用简短全小写的名字,但不建议用下划线。
(3)类名
类名一般使用首字母大写的约定。 在接口被文档化并且主要被用于调用的情况下,可以使用函数的命名风格代替。
(4)异常名
异常一般都是类,类的命名方法适用于异常名。但是,命名时需要在异常名后面加上“Error”后缀(如果异常确实是一个错误)。
(5)全局变量名
约定和函数命名规则一致。
(6)函数名
函数名应该小写,如果想提高可读性可以用下划线分隔。
(7)函数和方法参数
始终要将 self
作为实例方法的的第一个参数。
始终要将 cls
作为类静态方法的第一个参数。
如果函数的参数名和已有的关键词冲突,在最后加单一下划线比缩写或随意拼写更好。因此 class_
比 clss
更好。(也许最好用同义词来避免这种冲突)
(8)方法名和实例变量
遵循如下函数命名规则:使用下划线分隔小写单词以提高可读性。
在非共有方法和实例变量前使用单下划线。
通过双下划线前缀触发Python的命名转换规则来避免和子类的命名冲突。
(9)常量
通常通过下划线分隔的全大写字母命名。例如: MAX_OVERFLOW
和 TOTAL
。
代码应该用不损害其他Python实现的方式去编写。
比如,不要依赖于在CPython
中高效的内置字符连接语句 a += b 或者 a = a + b。
和像None这样的对象进行比较的时候应该始终用 is 或者 is not,永远不要用等号运算符。
另外,如果你在写 if x 的时候,应注意你是否表达的意思是 if x is not None。
使用 is not 运算符,而不是 not … is 。虽然这两种表达式在功能上完全相同,但前者在逻辑上更易于阅读,所以优先考虑。
推荐:
if foo is not None:
不推荐:
if not foo is None:
当使用富比较(rich comparisons,一种复杂的对象间比较的新机制,允许返回值不为-1,0,1)实现排序操作的时候,最好实现全部的六个操作符(__eq__
, __ne__
, __lt__
, __gt__
, __ge__
,__le__
)而不是依靠其他的代码去实现特定的比较。
始终使用def表达式,而不是通过赋值语句将lambda
表达式绑定到一个变量上。
推荐:
def f(x): return 2*x
不推荐:
f = lambda x: 2*x
第一个形式意味着生成的函数对象的名称是“f”而不是泛型“< lambda
>”。这在回溯和字符串显示的时候更有用。
从Exception继承异常,而不是BaseException
。直接继承BaseException
的异常适用于几乎不用来捕捉的异常。(BaseException
派生的异常是: GeneratorExit
, KeyboardInterrupt
, SystemExit
)
设计异常的等级,要基于捕捉异常代码的需要,而不是异常抛出的位置。以编程的方式去回答“出了什么问题?”,而不是只是确认“出现了问题”。(某处需要捕获异常时设计异常捕获,而不是出错以后才去设计捕获)
在处理异常的过程中,类的命名规范同样适用,如果异常是一个Error
的话,需要添加一个“Error”的后缀到你的异常类。
当捕获到异常时,如果可以的话写上具体的异常名,而不是只用一个except
: 块。 比如说:
try: import platform_specific_module except ImportError: platform_specific_module = None
如果只有一个except
: 块将会捕获到SystemExit
和KeyboardInterrupt
异常,这样会很难通过Control-C
中断程序,而且会掩盖掉其他问题。如果你想捕获所有指示程序出错的异常,使用 except Exception
: (只有except
等价于 except BaseException:
)。
以下两种情况不应该只使用‘excpet
’块:
如果异常处理的代码会打印或者记录log,至少让用户知道发生了一个错误。
如果代码需要做清理工作,使用 raise..try…finally 能很好处理这种情况并且能让异常继续上浮。
当给捕捉的异常绑定一个名字时,推荐使用在Python 2.6中加入的显式命名绑定语法:
try: process_data() except Exception as exc: raise DataProcessingFailedError(str(exc))
为了避免和原来基于逗号分隔的语法出现歧义,Python3只支持这一种语法。
当捕捉操作系统的错误时,推荐使用Python 3.3 中errno
内定数值指定的异常等级。
另外,对于所有的 try/except
语句块,在try语句中只填充必要的代码,这样能避免掩盖掉bug。 推荐:
try: value = collection[key] except KeyError: return key_not_found(key) else: return handle_value(value)
不推荐:
try: # Too broad! return handle_value(collection[key]) except KeyError: # Will also catch KeyError raised by handle_value() return key_not_found(key)
使用 ”.startswith
() 和 ”.endswith()
代替通过字符串切割的方法去检查前缀和后缀。 startswith()
和endswith()
更干净,出错几率更小。比如:
推荐: if foo.startswith('bar'): 糟糕: if foo[:3] == 'bar':
对象类型的比较应该用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):
不要用 ==
去和True
或者False
比较:
正确: if greeting: 糟糕: if greeting == True: 更糟: if greeting is True:
PEP 7, Style Guide for C Code, van Rossum ↩
PEP 8,Style Guide for Python CodePython PEP8 编码规范中文版_冒冒大虾的博客-CSDN博客_pep8 python 编码规范
Barry’s GNU Mailman style guide http://barry.warsaw.us/software/STYLEGUIDE.txt ↩
挂行缩进是一种类型设置样式,其中除第一行之外,段落中的所有行都缩进。在Python中,这个术语是用来描述一种风格:在被括号括起来的语句中,左括号是这一行最后一个非空格字符,随后括号内的内容每一行进行缩进,直到遇到右括号。 ↩
Donald Knuth’s The TeXBook, pages 195 and 196 ↩
Python3 错误和异常 | 菜鸟教程 ↩
Typeshed repo https://github.com/python/typeshed ↩
Suggested syntax for Python 2.7 and straddling code PEP 484 – Type Hints | peps.python.org ↩