文章目录
代码布局
缩进
- 每个缩进级别使用4个空格。
- 续行应该使用Python在括号、方括号和大括号内的隐式行连接,或者使用挂起的缩进来垂直对齐换行元素。使用挂起缩进时,应考虑以下内容:第一行不应有参数,应使用进一步的缩进来清楚地将自己区分为连续行。
- 应该的:
# 与开头分隔符对齐
foo = long_function_name(var_one, var_two,
var_three, var_four)
# 添加4个空格(额外的缩进级别)以区分参数与其他参数
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)
- 对于连续行,4空间规则是可选的。
- 可选:
# 悬挂缩进*可以*缩进到4个空格以外
foo = long_function_name(
var_one, var_two,
var_three, var_four)
- 当if语句的条件部分足够长,需要跨多行写入时,值得注意的是,两个字符的关键字(即if)加上一个空格,再加上左括号的组合会为多行条件的后续行创建自然的4空间缩进。这可能会与嵌套在if语句中的缩进代码集产生视觉冲突,该代码集也自然缩进为4个空格。对于如何(或是否)进一步直观地将这些条件行与if语句中的嵌套套件区分开来,这个PEP没有明确的立场。在这种情况下,可接受的选择包括但不限于:
# 没有额外的缩进
if (this_is_one_thing and
that_is_another_thing):
do_something()
# 添加注释,这将在编辑器中提供一些区别
# 支持语法突出显示
if (this_is_one_thing and
that_is_another_thing):
# 既然这两个条件都是真的,我们可以复述
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',
)
- 或者它可以排在开始多行构造的行的第一个字符下,如:
my_list = [
1, 2, 3,
4, 5, 6,
]
result = some_function_that_takes_arguments(
'a', 'b', 'c',
'd', 'e', 'f',
)
制表符还是空格?
- 空格是首选的缩进方法
- 制表符应仅用于与已缩进制表符的代码保持一致。
- Python 3不允许使用标签和空格来压痕。
- 以制表符和空格混合缩进的Python2代码应转换为只使用空格。
- 当使用-t选项调用Python2命令行解释器时,它会发出关于非法混合制表符和空格的代码的警告。使用-tt时,这些警告将变为错误。强烈推荐这些选项!
最大线长度
- 将所有行限制为最多79个字符。
- 对于结构限制较少的长文本块(docstring或comments),行长度应限制为72个字符。
- 限制所需的编辑器窗口宽度可以使多个文件并排打开,并且在使用代码审阅工具(在相邻列中显示这两个版本)时工作良好。
- 大多数工具中的默认包装会破坏代码的视觉结构,使其更难理解。选择这些限制是为了避免在窗口宽度设置为80的编辑器中换行,即使工具在换行时在最后一列中放置了标记图示符。一些基于web的工具可能根本不提供动态换行。
- 有些球队非常喜欢长线。对于专门维护或主要由团队维护的代码,如果注释和docstring仍被包装为72个字符,则可以将行长度限制增加到99个字符。
- Python标准库是保守的,要求将行限制为79个字符(docstrings/comments限制为72个字符)。
- 包装长行的首选方法是在括号、方括号和大括号内使用Python的隐含行继续符。通过将表达式括在圆括号中,可以在多行上打断长行。应该优先使用这些参数,而不是使用反斜杠作为行继续符。
- 有时反斜杠仍然是合适的。例如,long、multiple 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())
(有关使用语句缩进多行的进一步思考,请参阅前面关于多行if语句的讨论。)
- 另一个这样的例子是assert语句
- 确保适当缩进续行
在二进制运算符之前或之后应该换行吗?
- 几十年来,推荐的方式是在二进制运算符之后中断。但这会在两个方面损害可读性:操作符往往分散在屏幕上的不同列上,并且每个操作符都从其操作数移到前一行。在这里,眼睛必须做额外的工作来判断哪些项目是加的,哪些是减的:
# 否:运算符远离其操作数
income = (gross_wages +
taxable_interest +
(dividends - qualified_dividends) -
ira_deduction -
student_loan_interest)
- 为了解决这个可读性问题,数学家和他们的出版商遵循相反的惯例。Donald Knuth解释了他的计算机和排版系列中的传统规则:“尽管段落中的公式总是在二进制操作和关系之后断开,但显示的公式总是在二进制操作之前断开”
- 遵循数学的传统通常会产生更可读的代码:
# 是:易于将运算符与操作数匹配
income = (gross_wages
+ taxable_interest
+ (dividends - qualified_dividends)
- ira_deduction
- student_loan_interest)
- 在Python代码中,只要约定在本地保持一致,就允许在二进制运算符之前或之后中断。对于新的代码,建议使用Knuth的样式。
空行
- 用两个空行包围顶级函数和类定义。
- 类中的方法定义由一个空行包围。
- 额外的空行可以(谨慎地)用于分离相关函数组。在一堆相关的单行程序(例如,一组虚拟实现)之间可以省略空行。
- 在函数中尽量使用空行来表示逻辑部分。
- Python接受control-L(即^L)格式提要字符作为空白;许多工具将这些字符作为页分隔符,因此您可以使用它们来分隔文件相关节的页。注意,一些编辑器和基于web的代码查看器可能无法将control-L识别为表单提要,并将在其位置显示另一个标志符号。
源文件编码
- 核心Python发行版中的代码应该始终使用UTF-8(或Python 2中的ASCII)。
- 使用ASCII(在Python 2中)或UTF-8(在Python 3中)的文件不应该有编码声明。
- 在标准库中,非默认编码应仅用于测试目的,或者在注释或文档字符串需要提及包含非ASCII字符的作者名称时使用;否则,使用
\x
、\u
、\U
或\N
转义是在字符串文本中包含非ASCII数据的首选方法。 - 对于Python3.0及更高版本,为标准库规定了以下策略(请参见PEP 3131):Python标准库中的所有标识符必须仅使用ASCII标识符,并且在可行的情况下应使用英语单词(在许多情况下,使用的缩写和技术术语不是英语)。此外,字符串文本和注释也必须使用ASCII。唯一的例外是(a)测试非ASCII特性的测试用例,以及(b)作者姓名。姓名不基于拉丁字母(拉丁语-1,ISO/IEC 8859-1字符集)的作者必须在此字符集中提供其姓名的音译。
- 鼓励面向全球的开源项目采用类似的政策。
Imports
- import 通常应在单独的行上:
应该: import os
import sys
不应该: import sys, os
- 不过可以这么说:
from subprocess import Popen, PIPE
- 导入总是放在文件的顶部,在任何模块注释和docstring之后,在模块globals和常量之前。
- 导入应按以下顺序分组:
- 标准库导入
- 相关第三方进口
- 本地应用程序/库特定的导入
- 您应该在每组导入之间放置一个空行
- 建议绝对导入,因为如果导入系统配置不正确(例如包中的目录最终位于
sys.path
上),它们通常更具可读性,并且往往表现得更好(或至少给出更好的错误消息):
import mypkg.sibling
from mypkg import sibling
from mypkg.sibling import example
- 但是,显式相对导入是绝对导入的可接受替代方法,特别是在处理复杂的包布局时,使用绝对导入将不必要地冗长:
from . import sibling
from .sibling import example
- 标准库代码应避免复杂的包布局,并始终使用绝对导入
- 不应使用隐式相对导入,它已在Python3中被删除
- 从包含模块的类导入类时,通常可以拼写如下:
from myclass import MyClass
from foo.bar.yourclass import YourClass
- 如果此拼写导致本地名称冲突,请显式拼写它们:
import myclass
import foo.bar.yourclass
- 使用“myclass.myclass”和“foo.bar.yourclass.yourclass”
- 应避免使用通配符导入(从import*导入),因为通配符会使命名空间中出现的名称不清楚,从而混淆了读卡器和许多自动化工具。通配符导入有一个可防御的用例,即作为公共API的一部分重新发布内部接口(例如,使用可选加速器模块中的定义覆盖接口的纯Python实现,而具体要覆盖哪些定义尚不清楚)。
- 以这种方式重新发布名称时,以下有关公共和内部接口的指导原则仍然适用
Dunder名称级模块
- 模块级“dunders”(即带有两个前导下划线和两个尾随下划线的名称)例如
__all__, __author__, __version__, etc
等,应放在模块docstring之后,但放在除from __future__
导入之外的任何导入语句之前。Python要求将来的导入必须在除docstring之外的任何其他代码之前出现在模块中:
"""这是示例模块。
这个模块做一些事情。
"""
from __future__ import barry_as_FLUFL
__all__ = ['a', 'b', 'c']
__version__ = '0.1'
__author__ = 'Cardinal Biggles'
import os
import sys
字符串引号
- 在Python中,单引号字符串和双引号字符串是相同的。本政治公众人物并未就此提出建议。选择一条规则并坚持下去。但是,当字符串包含单引号或双引号字符时,请使用另一个引号以避免字符串中出现反斜杠。它提高了可读性。
- 对于三引号字符串,始终使用双引号字符以符合PEP 257中的docstring约定
表达式和语句中的空格
讨厌的事
- 避免如下情况的无关的空格
Yes: spam(ham[1], {eggs: 2})
No: 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]
- 在开始函数调用的参数列表的左括号之前:
Yes: spam(1)
No: spam (1)
- 在打开括号之前立即开始索引或切片:
Yes: dct['key'] = lst[index]
No: dct ['key'] = lst [index]
- 一个赋值运算符(或其他运算符)周围的多个空格,以使其与另一个运算符对齐
- 应该:
x = 1
y = 2
long_variable = 3
- 不应该:
x = 1
y = 2
long_variable = 3
其他建议
- 避免尾随空格。因为它通常是不可见的,所以可能会让人混淆:例如,反斜杠后跟空格和换行符不算作行继续标记。有些编辑器不保留它,许多项目(比如CPython本身)都有拒绝它的预提交挂钩。
- 始终在这些二进制运算符的两边用一个空格包围:赋值(=)、增广赋值(+、-=等)、比较(==,<,>,!=,<>,<=,>=,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)
- 函数注释应该使用冒号的常规规则,并且在->箭头周围始终有空格(如果有)。(有关函数注释的详细信息,请参见下面的函数注释。)
- 应该
def munge(input: AnyStr): ...
def munge() -> PosInt: ...
- 不应该
def munge(input:AnyStr): ...
def munge()->PosInt: ...
- 当用于指示关键字参数或用于指示未注释函数参数的默认值时,不要在=号周围使用空格
- 应该
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(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/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()
何时使用尾随逗号
- 尾随逗号通常是可选的,除非它们在生成一个元素的元组时是必需的(在Python 2中,它们具有print语句的语义)。为清楚起见,建议将后者括在(技术上多余的)括号中
- 应该:
FILES = ('setup.cfg',)
- 好吧,但让人困惑的是:
FILES = 'setup.cfg',
- 当尾随逗号是多余的时,当使用版本控制系统时,当值、参数或导入项的列表需要随着时间的推移而扩展时,它们通常是有用的。模式是将每个值(等)单独放在一行上,始终添加一个尾随逗号,并在下一行添加右括号/括号/大括号。但是,在结束分隔符所在的行上有一个尾随逗号是没有意义的(除了上面的单元组情况)。
- 应该
FILES = [
'setup.cfg',
'tox.ini',
]
initialize(FILES,
error=True,
)
- 不应该
FILES = ['setup.cfg', 'tox.ini',]
initialize(FILES, error=True,)
注释
- 与代码相矛盾的注释比没有注释更糟糕。在代码更改时,始终优先保持注释的最新状态!
- 注释应该是完整的句子。第一个单词应该大写,除非它是以小写字母开头的标识符(永远不要改变标识符的大小写!).
- 块注释通常由一个或多个段落组成,段落由完整的句子构成,每个句子以句号结尾。
- 在多句注释中,除了在最后一句之后,你应该在句尾句号后面使用两个空格。
- 在写英语时,请遵循Strunk和White。
- 来自非英语国家的Python代码编写者:请用英语编写您的注释,除非您有120%的把握确保不使用您的语言的人永远不会阅读这些代码。
块注释
- 块注释通常应用于跟随它们的某些(或全部)代码,并缩进到与该代码相同的级别。块注释的每一行以一个#和一个空格开头(除非它是注释内的缩进文本)。
- 块注释中的段落由包含单个
#
的行分隔。
行内注释
- 谨慎使用行内注释
- 行内注释是与语句位于同一行的注释。行内注释应至少与语句分隔两个空格。他们应该从一个单独的空间开始。
- 这样的行内注释是不必要的,事实上,如果它们陈述了显而易见的内容,就会分散注意力。不要这样做:
x = x + 1 # Increment x
- 但有时,这是有用的:
x = x + 1 # Compensate for border
文档字符串
- 用于编写良好文档字符串(也称为“docstrings”)的约定在PEP 257中永久化。
- 为所有公共模块、函数、类和方法编写docstring。对于非公共方法,docstring不是必需的,但是您应该有一个注释来描述该方法的作用。此注释应该出现在def行之后。
- PEP 257描述了良好的docstring约定。请注意,最重要的是,结束多行docstring的“”应该单独位于一行上:
"""Return a foobang
Optional plotz says to frobnicate the bizbaz first.
"""
- 对于一行docstrings,请将结束符
"""
保持在同一行
命名约定
- Python库的命名约定有点混乱,因此我们永远不会得到完全一致的结果——不过,下面是当前推荐的命名标准。新的模块和包(包括第三方框架)应该被写入这些标准,但是现有的库有不同的风格,内部一致性是首选
压倒一切的原则
- 作为API的公共部分对用户可见的名称应该遵循反映用法而不是实现的约定
描述性:命名样式
-
有很多不同的命名风格。它有助于识别正在使用的命名样式,独立于它们的用途
-
通常区分以下命名样式:
- b(单小写字母)
- B(单大写字母)
- 小写字母
- 带下划线的小写
- 大写
- 带下划线的大写
- 大写单词(或大写字母,或CamelCase——由于其字母的凹凸外观而得名)。这有时也被称为StudlyCaps
- 注意:在大写字母中使用缩略词时,请将缩略词的所有字母大写。因此,HTTPServerError优于HTTPServerError。
- mixedCase(不同于大写的小写字符!)
- 带下划线的大写单词(丑陋!)
-
还有一种使用短的唯一前缀将相关名称组合在一起的方式。这在Python中使用得不多,但为了完整起见,这里提到了这一点。例如,os.stat()函数返回一个元组,该元组的项通常有名称,如st_mode、st_size、st_mtime等等。(这样做是为了强调与POSIX系统调用结构的字段之间的对应关系,这有助于熟悉该结构的程序员。)
-
X11库的所有公共函数都使用前导X。在Python中,这种样式通常被认为是不必要的,因为属性和方法名以对象为前缀,函数名以模块名为前缀。
-
此外,还可以识别以下使用前导下划线或尾随下划线的特殊格式(这些格式通常可以与任何大小写约定结合使用):
- _单前导下划线:弱“内部使用”指标。E、 g.from M import*不导入名称以下划线开头的对象。
- 单尾随下划线:按惯例用于避免与Python关键字冲突,例如:
Tkinter.Toplevel(master, class_='ClassName')
- __双前导下划线:当命名一个类属性时,调用名称管理(在类FooBar中, __boo变为_FooBar__boo,如下所示)
__双_前导_和尾随_下划线__
:“魔法”对象或属性,它们位于用户控制的命名空间中。例如:__init__, __import__ or __file__
。永远不要发明这样的名字;只能用有文件记载的名字
规定性:命名约定
要避免的名字
- 切勿将字符“l”(小写字母el)、“O”(大写字母oh)或“I”(大写字母eye)用作单字符变量名
- 在某些字体中,这些字符与数字1和0无法区分。当试图使用“l”时,请改用“l”
ASCII兼容性
- 标准库中使用的标识符必须与PEP 3131的策略部分中所述的ASCII兼容
包和模块名称
- 模块应该有短的,所有小写的名称。如果提高了可读性,可以在模块名称中使用下划线。Python包还应该有短的、所有小写的名称,尽管不鼓励使用下划线
- 当用C或C++编写的扩展模块有一个伴随的Python模块,它提供一个更高级别(例如更多面向对象)的接口时,C/C++模块具有一个领先的下划线(例如,SOCKET)
类名
- 类名通常应该使用CapWords约定
- 函数的命名约定可用于记录接口并主要用作可调用接口的情况
- 注意,对于内置名称有一个单独的约定:大多数内置名称是单个单词(或两个单词一起运行),而CapWords约定只用于异常名称和内置常量
类型变量名
- PEP 484中引入的类型变量的名称通常应使用优先于短名称的大写字母:T、AnyStr、Num。建议在用于相应地声明协变或逆变行为的变量中添加后缀 _co 或者 _contra:
from typing import TypeVar
VT_co = TypeVar('VT_co', covariant=True)
KT_contra = TypeVar('KT_contra', contravariant=True)
异常名称
- 因为异常应该是类,所以这里应用类命名约定。但是,您应该在异常名称上使用后缀“Error”(如果异常实际上是一个错误)
全局变量名
- (希望这些变量只在一个模块中使用。)这些约定与函数的约定大致相同
- 设计用于从M导入**的模块应该使用γ-AlLuxi机制来防止导出全局,或者使用较低的下划线(例如,您可能想要做的来指示这些全局变量是“模块非公开”)的前缀
函数和变量名
- 函数名应为小写,必要时用下划线分隔单词以提高可读性
- 变量名遵循与函数名相同的约定
- mixedCase只允许在已经是流行样式(例如threading.py)的上下文中使用,以保持向后兼容
函数和方法参数
- 始终使用self作为实例方法的第一个参数
- 始终使用cls作为类方法的第一个参数
- 如果函数参数的名称与保留关键字冲突,通常最好附加一个尾随下划线,而不是使用缩写或拼写错误。因此类优于类。(最好是使用同义词来避免这种冲突。)
方法名和实例变量
- 使用函数命名规则:根据需要使用小写和下划线分隔的单词,以提高可读性
- 仅对非公共方法和实例变量使用一个前导下划线
- 为了避免名称与子类冲突,请使用两个前导下划线来调用Python的名称损坏规则
- Python用类名来替换这些名称:如果类Foo有一个名为“ __a”的属性,它就不能被
Foo.__a
访问。(坚持的用户仍然可以通过调用Foo来获得访问权限,Foo._Foo__a
)通常,双前导下划线只能用于避免名称与设计为子类的类中的属性冲突 - 注:关于使用
__names
名称有一些争议(见下文)
常数
- 常量通常在模块级定义,并用所有大写字母和下划线分隔单词。例如MAX_OVERFLOW和TOTAL
为继承而设计
-
始终决定类的方法和实例变量(统称为:“attributes”)应为public还是non-public。如果有疑问,请选择non-public;将其公开要比将public属性设置为non-public更容易
-
公共属性是那些您希望类中不相关的客户端使用的属性,并承诺避免向后不兼容的更改。非公共属性是不打算由第三方使用的属性;您不能保证非公共属性不会更改,甚至不会被删除
-
这里我们不使用术语“private”,因为在Python中没有属性是真正的private(没有通常不必要的工作量)
-
另一类属性是属于“子类API”(在其他语言中通常称为“protected”)的属性。有些类被设计为继承自,或者扩展或修改类行为的各个方面。在设计这样一个类时,请注意明确决定哪些属性是公共的,哪些是子类API的一部分,哪些真正只供基类使用
-
有鉴于此,以下是 python 的指南:
- 公共属性不应该有前导下划线
- 如果公共属性名与保留关键字冲突,请在属性名后面附加一个下划线。这比缩写或拼写错误更可取。(但是,尽管有此规则,“cls”是已知为类的任何变量或参数的首选拼写,尤其是类方法的第一个参数。)
注1:关于类方法,请参见上面的参数名建议。 - 对于简单的公共数据属性,最好只公开属性名,而不公开复杂的访问器/变异器方法。请记住,如果您发现一个简单的数据属性需要增加函数行为,那么Python提供了一个通向未来增强的简单路径。在这种情况下,使用属性隐藏简单数据属性访问语法后面的函数实现。
注1:属性仅适用于新样式的类
注意2:尽量使功能行为没有副作用,尽管诸如缓存之类的副作用通常很好
注意3:避免将属性用于计算开销较大的操作;属性表示法使调用者相信访问(相对)便宜 - 如果您的类是要作为子类的,并且您有不希望子类使用的属性,请考虑使用双前导下划线和无尾随下划线来命名它们。这将调用Python的名称管理算法,其中类的名称被管理到属性名中
注意1:请注意,在损坏的名称中只使用简单的类名,因此如果一个子类同时选择了相同的类名和属性名,则仍然可以得到名称冲突
注2:名称管理可能会有某些用途,例如调试和__getattr__()
,不太方便。但是,名称管理算法有很好的文档记录,并且易于手动执行
注3:不是每个人都喜欢名字乱七八糟。尽量平衡避免意外名称冲突的需要与高级呼叫者可能使用的名称冲突
公共和内部界面
- 任何后台兼容性保证只适用于公共界面。同样重要的是,用户可以明确区分公共和内部界面
- 文档化的接口被认为是公共的,除非文档明确声明它们是临时的或内部接口,不受通常的向后兼容性保证的约束,所有未确定的界面都应视为内部界面。
- 为了更好地支持内省,模块应该在其公共API中使用
__all__
属性显式地声明名称。将__all__
设置为空列表表示模块没有公共API - 即使设置了适当的
__all__
,内部接口(包、模块、类、函数、属性或其他名称)仍应以单个前导下划线作为前缀 - 一个界面还考虑到任何包含名称的内部界面(“包装”、“模块”或“类”)是内部界面。
- 导入的名称应始终视为实现详细信息。其他模块不得依赖于对这些导入名称的间接访问,除非它们是包含模块的API的显式文档部分,例如os.path或从子模块公开功能的包的
__init__
模块
编程建议
- 代码应以不妨碍 python(PyPy,Jython,Ironpython,Cython,Psyco 等)其他实现的方式写成
例如,不要依赖于CPython对a += b
或a = a + b
形式的语句的就地字符串连接的有效实现。这种优化即使在CPython中也很脆弱(它只适用于某些类型),而且在不使用再计数的实现中根本不存在。在库的性能敏感部分中,应改用''.join()
形式。这将确保在不同实现之间以线性时间进行连接 - 比较像不应该做的那样,做的是或不是,永远不应该是平等的操作者。
另外,当你用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),而不是依赖于其他代码只执行特定的比较
为了最小化所需的工作,functools.total_ordering()
装饰器提供了一个工具来生成缺少的比较方法
PEP 207表示由Python假定自反规则。因此,解释器可以将y > x
替换为x < y
,y >= x
替换为x <= y
,并且可以交换x == y
和x != y
的参数。sort()
和min()
操作保证使用<
运算符,max()
函数使用>
运算符。但是,最好实现所有六个操作,这样就不会在其他上下文中出现混淆 - 始终使用def语句,而不是直接将lambda表达式绑定到标识符的赋值语句
- 应该的:
def f(x): return 2*x
- 不应该:
f = lambda x: 2*x
- 第一种形式表示结果函数对象的名称是‘f’,而不是泛型
<lambda>
。这通常对于回溯和字符串表示更有用。使用赋值语句消除了lambda表达式相对于显式def语句所能提供的唯一好处(即它可以嵌入到更大的表达式中) - 从异常派生异常,而不是从BaseException派生异常。从BaseException直接继承是为异常保留的,在异常中捕获它们几乎总是错误的。
- 基于捕获异常的代码可能需要的区别而不是异常引发的位置来设计异常层次结构。目的是回答“出了什么问题?”以编程方式,而不仅仅是声明“出现了问题”(请参阅PEP 3151,以获取为内置异常层次结构学习的本课程示例)
- 这里应用类命名约定,但如果异常是错误,则应将后缀“Error”添加到异常类中。用于非本地流控制或其他形式的信令的非错误异常不需要特殊后缀。
- 适当地使用异常链接。在Python 3中,“raise X from Y”应该用于指示显式替换,而不会丢失原始的回溯。
- 故意替换内部异常时(在Python 2中使用“raise X”或在Python 3.3+中使用“raise X from None”),请确保将相关详细信息传输到新异常(例如在将KeyError转换为AttributeError时保留属性名,或在新异常消息中嵌入原始异常的文本)。
- 在Python2中引发异常时,请使用
raise ValueError('message')
,而不要使用旧格式raise ValueError,'message'
,后一种形式不是合法的Python3语法 - paren-using表单还意味着,当异常参数很长或包含字符串格式时,由于包含圆括号,因此不需要使用换行符
- 捕捉异常时,请尽可能提及特定异常,而不要使用纯
except:
子句:
try:
import platform_specific_module
except ImportError:
platform_specific_module = None
- 一个裸露的
except:
除外子句将捕获SturtExchange和KEKBOADE中断异常,这使得使用Control-C中断程序变得更加困难,并且可以掩盖其他问题。如果要捕获发出程序错误信号的所有异常,请使用except Exception:
(裸露的except
等同于except BaseException:
) - 一个好的经验法则是将“除外”条款的使用限制在两种情况下:
- 如果异常处理程序将打印或记录回溯,则至少用户将知道发生了错误
- 如果代码需要做一些清理工作,但随后会让异常随着
raise. try...finally
可以更好地处理这个案子了
- 将捕获到的异常绑定到名称时,请使用Python 2.6中添加的显式名称绑定语法:
try:
process_data()
except Exception as exc:
raise DataProcessingFailedError(str(exc))
- 这是Python 3中唯一支持的语法,避免了与旧的基于逗号的语法相关联的歧义问题。
- 在捕捉操作系统错误时,宁可使用Python 3.3中引入的显式异常层次结构,也不要对
errno
值进行内省。 - 此外,对于所有try/except子句,将try子句限制为所需的绝对最小代码量。同样,这样可以避免掩盖错误。
- 应该的:
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)
- 当资源是特定代码段的本地资源时,请使用with语句以确保在使用后及时可靠地清理该资源。try/finally语句也可以接受
- 上下文管理器应该通过单独的函数或方法调用,只要它们做的不是获取和释放资源
- 应该的:
with conn.begin_transaction():
do_stuff_in_transaction(conn)
- 不应该的:
with conn:
do_stuff_in_transaction(conn)
- 后一个示例不提供任何信息来指示
__enter__
和__exit__
方法正在执行除在事务后关闭连接之外的其他操作。在这种情况下,明确是很重要的。 - 在返回语句中保持一致。函数中的所有返回语句都应返回表达式,或者都不应返回表达式。如果任何return语句返回一个表达式,则没有返回值的任何return语句都应显式声明为
return None
,并且在函数的末尾(如果可以访问)应该有显式的return语句 - 应该的:
def foo(x):
if x >= 0:
return math.sqrt(x)
else:
return None
def bar(x):
if x < 0:
return None
return math.sqrt(x)
- 不应该:
def foo(x):
if x >= 0:
return math.sqrt(x)
def bar(x):
if x < 0:
return
return math.sqrt(x)
- 使用字符串方法而不是字符串模块。
- 字符串方法总是要快得多,并且与unicode字符串共享相同的API。如果需要与2.0以前的Pythons向后兼容,请重写此规则。
- 使用
''.startswith()
和''.endswith()
而不是字符串切片来检查前缀或后缀 - startswith() 和 endswith() 更干净,更不容易出错:
Yes: if foo.startswith('bar'):
No: if foo[:3] == 'bar':
- 对象类型比较应始终使用 isinstance() 而不是直接比较类型
应该的: if isinstance(obj, int):
不应该: if type(obj) is type(1):
- 检查对象是否为字符串时,请记住它也可能是unicode字符串!在Python 2中,str和unicode有一个公共基类basestring,因此可以执行以下操作:
if isinstance(obj, basestring):
- 注意,在Python 3中,unicode和basestring不再存在(只有str),bytes对象不再是一种字符串(而是一个整数序列)
- 对于序列(字符串、列表、元组),使用空序列为false的事实
应该的: if not seq:
if seq:
不应该: if len(seq):
if not len(seq):
- 不要编写依赖于重要尾随空白的字符串文本。这样的尾随空白在视觉上是不可区分的,一些编辑器(或者最近的 reindent.py)将对它们进行修剪
- 不要使用 == 与布尔值 True 或 False 进行比较
应该的: if greeting:
不应该: if greeting == True:
更糟的: if greeting is True:
- 不建议在
try...finally
套件中使用流控制语句 return/break/continue,因为流控制语句将跳出finally套件。这是因为这样的语句将隐式地取消通过finally套件传播的任何活动异常。 - 不应该:
def foo():
try:
1 / 0
finally:
return 42
函数注解
- 随着PEP 484的接受,函数注释的样式规则正在改变
- 为了向前兼容,Python 3代码中的函数注释最好使用PEP 484语法。(在上一节中有一些注释的格式建议。)
- 不再鼓励对之前在本PEP中推荐的注释样式进行试验
- 然而,在标准库之外,现在鼓励在PEP 484规则内进行实验。例如,使用PEP 484样式的类型注释标记大型第三方库或应用程序,检查添加这些注释的容易程度,并观察它们的存在是否提高了代码的可理解性。
- Python标准库在采用这样的注释时应该是保守的,但是允许在新代码和大型重构中使用它们。
- 对于希望使用不同函数注释的代码,建议放置表单注释:
# type: ignore
- 在文件顶部附近;这告诉类型检查器忽略所有注释。(在PEP 484中可以找到更细粒度的禁用类型检查程序投诉的方法。)
- 与剥绒机一样,类型检查器是可选的、独立的工具。默认情况下,Python解释器不应由于类型检查而发出任何消息,也不应基于注释更改其行为。
- 不想使用类型检查程序的用户可以自由忽略它们。但是,第三方库包的用户可能希望在这些包上运行类型检查程序。为此,PEP 484建议使用存根文件:.pyi 文件,这些文件由类型检查器读取,优先于相应的 .py 文件。存根文件可以与库一起分发,也可以通过打印的回购单独分发(经库作者许可)。
- 对于需要向后兼容的代码,可以以注释的形式添加类型注释。参见PEP 484的相关章节
变量注释
- PEP 526引入了变量注释。它们的样式建议类似于上面描述的函数注释:
- 模块级变量、类和实例变量以及局部变量的注释应该在冒号后有一个空格。
- 冒号前不应有空格。
- 如果赋值有右手边,则等号两边应正好有一个空格。
- 应该的:
code: int
class Point:
coords: Tuple[int, int]
label: str = '<unknown>'
- 不应该的:
code:int # No space after colon
code : int # Space before colon
class Test:
result: int=0 # No spaces around equality sign
- 尽管Python 3.6接受PEP 526,但变量注释语法是所有版本Python上存根文件的首选语法(有关详细信息,请参见PEP 484)
- 脚注:挂起缩进是一种类型设置样式,段落中除第一行外的所有行都缩进。在Python的上下文中,该术语用于描述一种样式,在这种样式中,带圆括号的语句的左括号是行的最后一个非空白字符,随后的行将缩进到右括号为止
来源:CSDN
作者:xiaogeldx
链接:https://blog.csdn.net/xiaogeldx/article/details/103779153