我最早了解PEP8时是在PyCharm的格式提醒中看到的。那时候初学Python语言,只知道Python由严格的缩进级别控制,也只是跟着老师敲代码的步伐一步一步模仿。我是在《Python从入门到精通》这本书上接触到Python。那时前几课的老师都是一个女老师。她那时候只是在IDLE上教如何敲代码,但是她的代码给人的感觉就是很工整,即使是纯手敲。因为她就遵从了PEP8规范之一——“#”注释要在该行代码最后一个字符后用两个空格隔开,且注释内容在“#”之后用一个空格隔开,即a = 1 # 注释xxx
。代码的可读性的重要性不言而喻,而每个人对代码的工整程度都有不同的定义。因此kennethreitz就总结出了一套Python代码编写规范。此规范目前被大众所认可,也被广泛学习。PEP8官网为pep8.org,此文章会挑选此网站中比较常用的规范,分享给大家。
代码布局
缩进
每个缩进级别使用4个空格。
连续行应该使用Python在圆括号、方括号和大括号内的隐式行连接或使用悬挂缩进。垂直对齐包装的元素当使用悬挂缩进时,应考虑以下事项:第一行不应有任何参数,并且应使用进一步的缩进以清楚地将其本身区分为继续行。
当函数参数列表过长而需要换行时,换到下一行的参数应与上一行的参数保持垂直对齐。若选择在第一个参数前就换行,则第一个参数前应缩进两个级别,以和参数下一行的代码缩进区分开。
正确的缩进:
# Aligned with opening delimiter.
foo = long_function_name(var_one, var_two,
var_three, var_four)
# More indentation included to distinguish this from the rest.
def long_function_name(
var_one, var_two, var_three,
var_four):
print(var_one)
# Hanging indents should add a level.
foo = long_function_name(
var_one, var_two,
var_three, var_four)
错误的缩进:
# Arguments on first line forbidden when not using vertical alignment.
foo = long_function_name(var_one, var_two,
var_three, var_four)
# Further indentation required as indentation is not distinguishable.
def long_function_name(
var_one, var_two, var_three,
var_four):
print(var_one)
可选择短于4空格的缩进。
可选:
# Hanging indents *may* be indented to other than 4 spaces.
foo = long_function_name(
var_one, var_two,
var_three, var_four)
当if-语句的条件部分足够长,需要跨多行编写时,值得注意的是,两个字符的关键字(即if),加上一个空格,加上一个左括号,为多行条件的后续行创建了一个自然的4空格缩进。这可能会与嵌套在if-语句中的缩进代码套件产生视觉冲突,该代码套件自然也会缩进4个空格。该PEP没有明确说明如何(或是否)进一步在视觉上将这些条件行与if-语句内的嵌套套件区分开来。在这种情况下,可接受的选项包括但不限于:
# No extra indentation.
if (this_is_one_thing and
that_is_another_thing):
do_something()
# Add a comment, which will provide some distinction in editors
# supporting syntax highlighting.
if (this_is_one_thing and
that_is_another_thing):
# Since both conditions are true, we can frobnicate.
do_something()
# Add some extra indentation on the conditional continuation line.
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',
)
选择空格还是选择制表符
空格是首选的缩进方法。
Python 3不允许在缩进中混合使用制表符和空格。
最大行长度
将所有行限制为最多79个字符。
对于具有较少结构限制(文档字符串或注释)的长文本块,行长度应限制为72个字符。
限制所需的编辑器窗口宽度可以使多个文件并排打开,并且在使用代码评审工具在相邻列中显示两个版本时效果很好。
大多数工具中的默认包装会破坏代码的可视结构,使其更难理解。选择这些限制是为了避免在窗口宽度设置为80的编辑器中换行,即使换行时工具在最后一列中放置标记图示符。一些基于web的工具可能根本不提供动态换行。
一些团队强烈喜欢更长的线长度。对于专门或主要由能够就此问题达成一致的团队维护的代码,可以将标称行长度从80个字符增加到100个字符(有效地将最大长度增加到99个字符),前提是注释和文档字符串仍然以72个字符包装。
换行的首选方法是在圆括号、方括号和大括号内使用Python的隐含行延续。通过将表达式括在括号中,可以将长行拆分为多行。这些应该优先于使用反斜杠来继续行。
反斜杠有时可能仍然是合适的。例如,长的多个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())
二元运算符在何处打破
在Python代码中,允许在二元运算符之前或之后中断,只要约定在本地保持一致。对于新的代码,建议遵循Knuth的风格,即在运算符前打断。
# Yes: easy to match operators with operands
income = (gross_wages
+ taxable_interest
+ (dividends - qualified_dividends)
- ira_deduction
- student_loan_interest)
空白行
用两个空行包围顶级函数和类定义。
类中的方法定义由一个空行包围。
额外的空白行可以用来(有节制地)分隔相关函数的组。在一堆相关的一行代码之间可以省略空行(例如一组虚拟实现)。
在函数中尽量少用空行来表示逻辑部分。
import导入
若有多个导入则应该分行。
正确:
import os
import sys
错误:
import os, sys
但限定导入可以放在一行。
正确:
from subprocess import Popen, PIPE
导入总是放在文件的顶部,就在所有模块注释和文档字符串之后,在模块全局值和常量之前。
导入应按以下顺序分组:
- 标准库导入
- 相关第三方进口
- 本地应用程序/库特定导入
您应该在每组导入之间放置一个空行。
推荐使用绝对导入,因为它们可以使变量具有更好的可读性,也会有更少的错误提示。
import mypkg.sibling
from mypkg import sibling
from mypkg.sibling import example
若显式绝对导入过于冗余,可选用显式相对导入。
from . import sibling
from .sibling import example
标准库代码应避免复杂的包布局,并始终使用绝对导入。
隐式相对导入永远不应该使用,并且在Python 3中已经删除了。
当从包含类的模块导入类时,通常可以拼写如下:
from myclass import MyClass
from foo.bar.yourclass import YourClass
若上方的导入会导致命名冲突,可以使用如下的导入方式:
import myclass
import foo.bar.yourclass
然后使用myclass.MyClass
和foo.bar.yourclass.YourClass
。
应该避免使用通配符导入(from
表达式和语句中的空格
在以下情况下避免多余的空格:
直接在圆括号、方括号或大括号内:
正确:
spam(ham[1], {eggs: 2})
错误:
spam( ham[ 1 ], { eggs: 2 } )
在尾随逗号和后面的右括号之间:
正确:
foo = (0,)
错误:
bar = (0, )
紧接逗号、分号或冒号之前:
正确:
if x == 4: print x, y; x, y = y, x
错误:
if x == 4 : print x , y ; x , y = y , x
然而,在切片中冒号的作用就像一个二元运算符,并且应该在两侧具有相等的数量(将其视为具有最低优先级的运算符)。在扩展切片中,两个冒号必须应用相同的间距。例外:当省略切片参数时,空格将被省略。
正确:
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]
紧接在开始函数调用参数列表的左括号之前:
正确:
spam(1)
错误:
spam (1)
紧接在开始索引或切片的左括号之前:
错误:
dct['key'] = lst[index]
错误:
dct ['key'] = lst [index]
赋值(或其他)运算符周围有多个空格,用于将其与另一个运算符对齐。
正确:
x = 1
y = 2
long_variable = 3
错误:
x = 1
y = 2
long_variable = 3
其他空格
避免在任何地方尾随空白。因为它通常是不可见的,所以可能会令人困惑:例如,后面跟有空格和换行符的反斜杠不算作行继续标记。
始终在这些二元运算符的两侧使用单个空格:赋值(=)、增广赋值(+=、-=等)、比较(==、<、>、!=、<>、<=、>=、in、not in、is、is not)、布尔(and、or、not)。
正确:
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)
如果使用具有不同优先级的运算符,请考虑在具有最低优先级的运算符周围添加空白。用你自己的判断;但是,永远不要使用一个以上的空格,并且始终在二元运算符的两边具有相同数量的空白。
正确:
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)
当用于指示关键字参数或默认参数值时,不要在=符号周围使用空格。
正确:
def complex(real, imag=0.0):
return magic(r=real, i=imag)
错误:
def complex(real, imag = 0.0):
return magic(r = real, i = imag)
函数注释应该使用冒号的常规规则,并且在->箭头周围总是有空格(如果存在)。
正确:
def munge(input: AnyStr): ...
def munge() -> AnyStr: ...
错误:
def munge(input:AnyStr): ...
def munge()->PosInt: ...
将参数注释与默认值组合时,在=符号周围使用空格(但仅适用于既有注释又有默认值的参数)。
正确:
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): ...
复合语句(同一行上的多个语句)通常不受欢迎。
正确:
if foo == 'blah':
do_blah_thing()
do_one()
do_two()
do_three()
错误:
if foo == 'blah': do_blah_thing()
do_one(); do_two(); do_three()
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()
注释
内联注释是与语句位于同一行的注释。内联注释应与语句至少用两个空格分隔。它们应该以#和一个空格开头。
内联注释是不必要的,事实上,如果它们陈述了显而易见的东西,就会分散注意力。
这样做毫无意义:
i = i + 1 # 将i的值增1
注释应该多使用文档字符串。有关更多文档字符串的知识请阅读下面的文章。
{cat_insidepost id="216"}
命名
命名规则
有以下几种常见的命名:
- b (单个小写字母)
- B (单个大写字母)
- lowercase(小写英文单词)
- lower_case_with_underscores(带下画线的小写单词)
- UPPERCASE(全部大写)
- UPPER_CASE_WITH_UNDERSCORES(带下画线的大写单词)
- CapitalizedWords (or CapWords, CamelCase5, StudlyCaps)(驼峰命名法)
- mixedCase (和 CapitalizedWords 区分开)
- Capitalized_Words_With_Underscores (小丑命名)
在CapWords中使用缩写时,请将缩写的所有字母大写。HTTPServerError比HttpServerError好。
还有一种风格是使用一个简短的唯一前缀将相关名称分组在一起。这在Python中使用得不多,但为了完整性而提到它。例如,os.stat()函数返回一个元组,其项传统上具有诸如st_mode、st_size、st_mtime等的名称。(这样做是为了强调与POSIX系统调用struct的字段的对应关系,这有助于程序员熟悉它。
此外,还可以识别以下使用前导或尾随下划线的特殊形式(这些形式通常可以与任何大小写约定组合使用):
_single_leading_underscore:弱“内部使用”指示符。例如,from M import *不会导入名称以下划线开头的对象。
single_trailing_underscore_:约定使用,避免与Python关键字冲突,例如:def f(class_=None)
当一个用C或C++编写的扩展模块有一个附带的Python模块,提供更高级别的(例如更面向对象的)接口,C/C++模块具有前导下划线(例如:_socket)。
类名
类名通常应使用CapWords约定。
在接口被记录并主要用作可调用的情况下,可以使用函数的命名约定。
请注意,内置名称有一个单独的约定:大多数内建名称是单个单词(或两个单词一起运行),CapWords约定仅用于异常名称和内建常量。
设计用于通过from M import *使用的模块应该使用__all__机制来防止导出全局变量,或者使用旧的约定,即在这些全局变量的前缀加上下划线(您可能希望这样做以表明这些全局变量是“模块非公共的”)。
函数名
函数名应该是小写的,必要时用下划线分隔单词,以提高可读性。
mixedCase只允许在已经是流行风格的上下文中使用(例如threading.py),以保持向后兼容性。
总是使用self作为实例方法的第一个参数。
始终使用cls作为类方法的第一个参数。
如果函数参数的名称与保留关键字冲突,通常最好在后面附加一个下划线,而不是使用缩写或拼写错误。class_比clss好。(也许更好的做法是使用同义词来避免这种冲突。
常量
常量通常在模块级别上定义,并以大写字母书写,下划线分隔单词。示例包括MAX_OVERFLOW和TOTAL。
其他规范
像None这样的单例比较应该总是用is或is not来完成,而不是相等运算符。
此外,当你真正的意思是if x时,小心写if x is not None-例如。当测试默认值为None的变量或参数是否设置为其他值时。另一个值可能有一个类型(例如容器),在布尔上下文中可能是false!
使用is not运算符而不是not ... is。虽然这两种表达在功能上相同,但前者更易于阅读,更推荐。
正确:
if foo is not None:
错误:
if not foo is None:
当实现具有丰富比较的排序操作时,最好实现所有六个操作运算符的魔法函数(__eq__、__ne__、__lt__、__le__、__gt__、__ge__),而不是依赖于其他代码来执行特定的比较。有关魔法函数的文章请阅读下文。
{cat_insidepost id="141"}
从Exception而不是BaseException导出异常。从BaseException的直接继承是为异常保留的,因为捕获它们几乎总是错误的。
根据捕获异常的代码可能需要的区别来设计异常层次结构,而不是根据引发异常的位置。目的是回答问题“哪里出错了?
一个空的except:子句将捕获SystemExit和KeyboardInterrupt异常,这使得使用Control-C中断程序变得更加困难,并且可以掩盖其他问题。如果你想捕获所有的异常,可以使用except Exception:(bare except相当于except BaseException:)。
一个好的经验法则是将裸“except”子句的使用限制在两种情况下:
- 如果异常处理程序将打印或记录跟踪;至少用户将意识到错误已经发生。
- 如果代码需要做一些清理工作,但随后让异常向上传播raise。try...finally可以更好地处理这个问题。
上下文管理器在执行获取和释放资源以外的操作时,都应该通过单独的函数或方法调用。举例来说:
正确:
with conn.begin_transaction():
do_stuff_in_transaction(conn)
错误:
with conn:
do_stuff_in_transaction(conn)