PEP 8 – Python 代码风格指南

猫勺猫勺 02-20 282 阅读 0 评论

介绍

本文档给出了 Python 代码编码约定,包括 主 Python 发行版中的标准库。请参阅 描述 C 代码样式指南的配套信息 PEP 在 Python 的 C 实现中

本文档和 PEP 257(文档字符串约定)改编自 Guido 的原始 Python 风格指南文章,以及一些来自 Barry的风格指南。

此样式指南会随着其他约定而随着时间的推移而发展 已确定的和过去的约定因以下方面的更改而过时 语言本身。

许多项目都有自己的编码风格指南。如果发生任何情况 冲突时,此类特定于项目的指南优先于该项目。

愚蠢的一致性是小头脑的妖精

Guido 的一个关键见解是,代码的阅读频率远高于 它被写了。此处提供的指南旨在改进 代码的可读性,并使其在宽广范围内保持一致 Python 代码的频谱。正如 PEP 20 所说,“可读性很重要”。

风格指南是关于一致性的。与本风格指南保持一致 很重要。项目内的一致性更为重要。 一个模块或功能内的一致性是最重要的。

但是,要知道何时不一致——有时是风格指南 建议只是不适用。如有疑问,请尽力而为 判断。查看其他示例并确定看起来最好的示例。和 不要犹豫,问!

特别是:不要仅仅为了遵守而破坏向后兼容性 这个PEP!

忽略特定准则的其他一些充分理由:

当应用该指南时,即使会降低代码的可读性 对于习惯于阅读遵循此 PEP 的代码的人。

为了与周围的代码保持一致,这些代码也会破坏它(也许 出于历史原因)——尽管这也是一个机会 清理别人的烂摊子(以真正的XP风格)。

因为所讨论的代码早于 指南,并且没有其他理由修改该代码。

当代码需要与旧版本的 不支持样式指南推荐的功能的 Python。

代码布局

凹痕

每个缩进级别使用 4 个空格。

延伸线应垂直对齐包装的元素 使用 Python 的隐式行连接在括号、括号和 大括号,或使用悬挂缩进 。使用悬挂时 缩进应考虑以下事项;不应该有 第一行上的参数和进一步的缩进应该用于 清楚地将自己区分为延续线:

# Correct:# Aligned with opening delimiter.foo = long_function_name(var_one, var_two,
                         var_three, var_four)# Add 4 spaces (an extra level of indentation) to distinguish arguments 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)
# Wrong:# 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)

当 -statement 的条件部分足够长,需要 它被写在多行中,值得注意的是 两个字符关键字(即)加上一个空格的组合, 此外,左括号还为 多行条件的后续行。这可以产生视觉对象 与嵌套在 -语句中的缩进代码套件冲突, 这也自然会缩进到 4 个空格。这个 PEP 不需要 关于如何(或是否)进一步在视觉上区分此类的明确立场 -语句中嵌套套件中的条件行。 在这种情况下,可接受的选项包括但不限于:ifififif

# 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 不允许混合使用制表符和空格进行缩进。

最大线长

将所有行限制为最多 79 个字符。

用于具有较少结构限制的长文本块 (文档字符串或注释),行长应限制为 72 行 字符。

限制所需的编辑器窗口宽度可以 多个文件并排打开,使用代码时效果很好 查看在相邻列中显示两个版本的工具。

大多数工具中的默认包装会破坏 代码,使其更难理解。选择的限制是 避免在窗口宽度设置为 80 的情况下换行编辑器,即使 如果工具在换行时将标记字形放在最后一列中 线。某些基于 Web 的工具可能根本不提供动态换行。

一些团队强烈喜欢更长的线路长度。对于维护的代码 完全或主要由能够就此达成协议的团队 问题,可以将行长限制增加到 99 个字符, 前提是注释和文档字符串仍包装在 72 字符。

Python 标准库是保守的,需要限制 行数为 79 个字符(文档字符串/注释为 72 个字符)。

换行长行的首选方法是使用 Python 的隐含 括号、括号和大括号内的行延续。排长队 可以通过将表达式包装在 括弧。这些应优先使用,而不是使用反斜杠 用于行延续。

反斜杠有时可能仍然合适。例如,long、 多个 -语句不能使用隐式延续 在 Python 3.10 之前,在这种情况下,反斜杠是可以接受的: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 语句的讨论 关于这种多行语句的缩进的想法。with

另一个这样的情况是语句。assert

确保适当地缩进连续行。

二进制运算符之前还是之后应该换行?

几十年来,推荐的风格是在二进制运算符之后中断。 但这会在两个方面损害可读性:操作员往往会得到 分散在屏幕上的不同列中,每个运算符都是 从其操作数移动到上一行。在这里,眼睛 必须做额外的工作来判断哪些项目是添加的,哪些是 减去:

# Wrong:# operators sit far away from their operandsincome = (gross_wages +
          taxable_interest +
          (dividends - qualified_dividends) -
          ira_deduction -
          student_loan_interest)

为了解决这个可读性问题,数学家和他们的出版商 遵循相反的约定。唐纳德·克努斯(Donald Knuth)解释了传统的 在他的《计算机和排版》系列中的规则:“虽然公式 在一个段落中,总是在二进制运算和关系之后中断, 显示的公式总是在二进制操作之前中断“。

遵循数学的传统通常会导致更多 可读代码:

# Correct:# easy to match operators with operandsincome = (gross_wages
          + taxable_interest
          + (dividends - qualified_dividends)
          - ira_deduction
          - student_loan_interest)

在 Python 代码中,允许在二进制文件之前或之后中断 运算符,只要约定在本地保持一致即可。对于新的 建议使用 Knuth 的风格。

空行

用两个空白将顶级函数和类定义括起来 线。

类内的方法定义被单个空白包围 线。

可以使用额外的空行(谨慎地)来分隔 相关功能。一堆空行之间可能会省略 相关的单行代码(例如,一组虚拟实现)。

在函数中谨慎使用空行来指示逻辑部分。

Python 接受 control-L(即 ^L)形式的 feed 字符作为 空白;许多工具将这些字符视为页面分隔符,因此 您可以使用它们来分隔文件相关部分的页面。 请注意,某些编辑器和基于 Web 的代码查看者可能无法识别 control-L 作为表单提要,并将在其位置显示另一个字形。

源文件编码

核心 Python 发行版中的代码应始终使用 UTF-8,不应使用 UTF-8 具有编码声明。

在标准库中,非 UTF-8 编码应仅用于 测试目的。谨慎使用非 ASCII 字符,最好只使用 表示地点和人名。如果使用非 ASCII 字符作为数据, 避免使用嘈杂的 Unicode 字符,如 z̯̯͡a̧͎̺l̡͓̫g̹̲o̡̼̘ 和字节顺序 标志着。

Python 标准库中的所有标识符必须仅使用 ASCII 标识符,并应尽可能使用英语单词(在许多 使用案例、缩写和技术术语,但不 英语)。

鼓励具有全球受众的开源项目采用 类似的政策。

进口

进口通常应在单独的行上:

# Correct:import osimport sys
# Wrong:import sys, os

不过可以这样说:

# Correct:from subprocess import Popen, PIPE

导入始终放在文件的顶部,紧跟在任何模块之后 注释和文档字符串,以及模块全局变量和常量之前。

导入应按以下顺序分组:

您应该在每组导入之间放置一个空行。

标准库导入。

相关第三方进口。

特定于本地应用程序/库的导入。

建议使用绝对导入,因为它们通常更具可读性 并且往往表现得更好(或者至少给出更好的错误 messages),如果导入系统配置不正确(例如 当包内的目录最终位于 ):sys.path

import mypkg.siblingfrom mypkg import siblingfrom mypkg.sibling import example

但是,显式相对导入是可以接受的替代方法 绝对导入,尤其是在处理复杂的包布局时 使用绝对导入会不必要地冗长:

from . import siblingfrom .sibling import example

标准库代码应避免复杂的包布局,并且始终 使用绝对导入。

从包含类的模块导入类时,通常 可以拼写这个:

from myclass import MyClassfrom foo.bar.yourclass import YourClass

如果此拼写导致本地名称冲突,请显式拼写:

import myclassimport foo.bar.yourclass

并使用 和 .myclass.MyClassfoo.bar.yourclass.YourClass

应避免使用通配符导入 (),因为 它们使命名空间中存在哪些名称变得不清楚, 使读者和许多自动化工具都感到困惑。有一个 通配符导入的可防御用例,即重新发布 内部接口作为公共 API 的一部分(例如,覆盖 带有定义的接口的纯 Python 实现 从可选的加速器模块以及确切的定义 将被覆盖事先不知道)。from <module> import *

以这种方式重新发布名称时,以下关于 公共接口和内部接口仍然适用。

模块级 Dunder 名称

模块级“dunders”(即具有两个前导和两个尾随的名称 下划线),例如 , , , 等应放在模块文档字符串之后,但在任何导入之前 除 imports 之外的语句。Python 要求 future-imports 必须出现在模块中的任何其他代码之前,除非 文档字符串:__all____author____version__from __future__

"""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 osimport sys

字符串引号

在 Python 中,单引号字符串和双引号字符串是 相同。本 PEP 不对此提出建议。选择规则 并坚持下去。当字符串包含单引号或双引号时 但是,字符使用另一个字符来避免 字符串。它提高了可读性。

对于三引号字符串,请始终使用双引号字符 与 PEP 257 中的文档字符串约定一致。

表达式和语句中的空格

宠物烦恼

在以下情况下,请避免多余的空格:

紧挨着括号、括号或大括号内:

# Correct:spam(ham[1], {eggs: 2})
# Wrong:spam( ham[ 1 ], { eggs: 2 } )

在尾随逗号和后面的右括号之间:

# Correct:foo = (0,)
# Wrong:bar = (0, )

紧接在逗号、分号或冒号之前:

# Correct:if x == 4: print(x, y); x, y = y, x
# Wrong:if x == 4 : print(x , y) ; x , y = y , x

但是,在切片中,冒号的作用类似于二进制运算符,并且 两边都应该有相等的金额(将其视为 具有最低优先级的运算符)。在扩展切片中,两者 冒号必须应用相同的间距。例外: 省略 slice 参数时,将省略空格:

# Correct: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]
# Wrong:ham[lower + offset:upper + offset]ham[1: 9], ham[1 :9], ham[1:9 :3]ham[lower : : step]ham[ : upper]

紧接在开始参数的左括号之前 函数调用列表:

# Correct:spam(1)
# Wrong:spam (1)

紧接在开始索引的左括号之前,或者 切片:

# Correct:dct['key'] = lst[index]
# Wrong:dct ['key'] = lst [index]

赋值(或其他)运算符周围的多个空格 将其与另一个对齐:

# Correct:x = 1y = 2long_variable = 3
# Wrong:x             = 1y             = 2long_variable = 3


其他建议

避免在任何地方尾随空格。因为它通常是看不见的, 它可能会令人困惑:例如,反斜杠后跟空格和 换行符不算作行延续标记。一些编辑 不要保留它,许多项目(如 CPython 本身)都有 拒绝它的预提交钩子。

始终用一个空格将这些二进制运算符括起来 side:赋值 ()、增强赋值 (等)、比较 (、 、 、 、=+=-===<>!=<><=>=innot inisis notandornot

如果使用不同优先级的运算符,请考虑添加 优先级最低的运算符周围的空格。用 你自己的判断;但是,切勿使用多个空格,并且 在二进制文件的两端始终具有相同数量的空格 算子:

# Correct:i = i + 1submitted += 1x = x*2 - 1hypot2 = x*x + y*yc = (a+b) * (a-b)
# Wrong:i=i+1submitted +=1x = x * 2 - 1hypot2 = x * x + y * yc = (a + b) * (a - b)

函数注释应使用冒号和 箭头周围始终有空格(如果存在)。(有关函数注解的更多信息,请参阅下面的函数注解。->

# Correct:def munge(input: AnyStr): ...def munge() -> PosInt: ...
# Wrong:def munge(input:AnyStr): ...def munge()->PosInt: ...

当用于表示 关键字参数,或用于指示未批注函数参数的默认值时:=

# Correct:def complex(real, imag=0.0):    
        return magic(r=real, i=imag)
# Wrong:def complex(real, imag = 0.0):
    return magic(r = real, i = imag)

但是,当将参数注释与默认值组合在一起时,请使用 标志周围的空格:=

# Correct:def munge(sep: AnyStr = None): ...def munge(input: AnyStr, sep: AnyStr = None, limit=1000): ...
# Wrong:def munge(input: AnyStr=None): ...def munge(input: AnyStr, limit = 1000): ...

复合语句(同一行上的多个语句)是 通常不鼓励:

# Correct:if foo == 'blah':
    do_blah_thing()do_one()do_two()do_three()

而不是:

# Wrong:if foo == 'blah': do_blah_thing()do_one(); do_two(); do_three()

虽然有时可以用小身体放置一个 if/for/while 在同一行上,永远不要对多子句语句执行此操作。也 避免折叠这么长的线!

而不是:

# Wrong:if foo == 'blah': do_blah_thing()for x in lst: total += xwhile t < 10: t = delay()

绝对不是:

# Wrong: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()


何时使用尾随逗号

尾随逗号通常是可选的,但在以下情况下它们是必需的 创建一个元素的元组。为清楚起见,建议 将后者括在(技术上是多余的)括号中:

# Correct:FILES = ('setup.cfg',)
# Wrong:FILES = 'setup.cfg',

当尾随逗号是多余的时,它们通常很有帮助,当 使用版本控制系统时,当值、参数或 导入的项目预计将随着时间的推移而延长。模式是 将每个值(等)单独放在一行上,始终添加尾随 逗号,并在下一行添加右括号/括号/大括号。 但是,在同一上面有一个尾随逗号是没有意义的 行作为结束分隔符(上述单例情况除外) 元组):

# Correct:FILES = [
    'setup.cfg',
    'tox.ini',
    ]initialize(FILES,
           error=True,
           )
# Wrong:FILES = ['setup.cfg', 'tox.ini',]initialize(FILES, error=True,)


评论

与代码相矛盾的注释比没有注释更糟糕。总是 当代码出现时,优先使注释保持最新状态 变化!

评论应该是完整的句子。第一个词应该是 大写,除非它是以小写字母开头的标识符 字母(永远不要改变标识符的大小写!

块注释通常由一个或多个段落组成,这些段落由 完整的句子,每个句子都以句号结尾。

您应该在句子结束句号后使用一个或两个空格 多句注释,最后一句除外。

确保您的评论清晰易懂,易于其他人理解 使用您正在写作的语言。

来自非英语国家的 Python 编码人员:请写下你的 英文评论,除非您 120% 确定代码永远不会 被不会说你的语言的人阅读。

阻止评论

阻止注释通常适用于以下部分(或全部)代码 它们,并缩进到与该代码相同的级别。每一行 阻止注释以 a 和一个空格开头(除非它是 注释中的缩进文本)。#

块注释中的段落由包含 单。#

内联注释

谨慎使用内联注释。

内联注释是与语句位于同一行的注释。 内联注释应与 陈述。它们应该以 # 和单个空格开头。

内联注释是不必要的,如果它们声明,实际上会分散注意力 显而易见的。别这样:

x = x + 1                 # Increment x

但有时,这很有用:

x = x + 1                 # Compensate for border


文档字符串


编写良好文档字符串的约定 (又名“文档字符串”)在 PEP 257 中永垂不朽。

为所有公共模块、函数、类和 方法。文档字符串对于非公共方法不是必需的,但是 您应该有一个注释来描述该方法的作用。这 注释应出现在该行之后。def

PEP 257 描述了良好的文档字符串约定。请注意,大多数 重要的是,结束多行文档字符串的 在一行本身上:"""

"""Return a foobangOptional plotz says to frobnicate the bizbaz first."""

对于一个衬里文档字符串,请保持关闭 同一条线:"""

"""Return an ex-parrot."""


命名约定

Python 库的命名约定有点乱,所以 我们永远不会得到完全一致的——然而,这里是 当前推荐的命名标准。新模块和软件包 (包括第三方框架)应该写到这些 标准,但现有库具有不同的风格, 内部一致性是首选。

压倒一切的原则

作为 API 的公共部分对用户可见的名称应 遵循反映使用情况而不是实现的约定。

描述性:命名样式

有很多不同的命名样式。它有助于能够 识别正在使用的命名样式,而与使用什么无关 它们用于。

通常区分以下命名样式:

b(单个小写字母)

B(单个大写字母)

lowercase

lower_case_with_underscores

UPPERCASE

UPPER_CASE_WITH_UNDERSCORES

CapitalizedWords(或 CapWords,或 CamelCase——之所以如此命名,是因为 其字母的凹凸不平的外观[4])。这有时也是众所周知的 饰演 StudlyCaps。

注意:在 CapWords 中使用首字母缩略词时,请将所有 首字母缩略词的字母。因此,HTTPServerError 优于 HttpServerError。

mixedCase(与 CapitalizedWords 的首字母小写不同 性格!

Capitalized_Words_With_Underscores(丑陋!

还有一种风格是使用简短的唯一前缀来分组相关 名称在一起。这在 Python 中用得不多,但提到了 为了完整性。例如,该函数返回一个 元组,其项传统上具有 、 等名称。(这样做是为了强调 与 POSIX 系统调用结构的字段对应,其中 帮助程序员熟悉这一点。os.stat()st_modest_sizest_mtime

X11 库使用前导 X 来实现其所有公共函数。在 Python,这种风格通常被认为是不必要的,因为属性 方法名称以对象为前缀,函数名称为 以模块名称为前缀。

此外,以下特殊形式使用前导或尾随 下划线是可识别的(这些下划线通常可以与任何 案例惯例):

_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__

规范性:命名约定

要避免的名称

切勿使用字符“l”(小写字母 el)、“O”(大写字母) 字母 oh)或“I”(大写字母 eye)作为单字符变量 名字。

在某些字体中,这些字符与 数字 1 和 0。当试图使用“l”时,请改用“L”。

ASCII 兼容性

标准库中使用的标识符必须与 ASCII 兼容 如 PEP 3131 的政策部分所述。

封装和模块名称

模块应具有简短的全小写名称。下划线可以是 如果提高可读性,则在模块名称中使用。Python 包 还应该有简短的全小写名称,尽管使用 不鼓励使用下划线。

当用 C 或 C++ 编写的扩展模块具有随附的 提供更高级别的 Python 模块(例如,更面向对象) 接口,C/C++模块有一个前导下划线 (例如)。_socket

类名

类名通常应使用 CapWords 约定。

在以下情况下,可以改用函数的命名约定 该接口被记录下来,主要用作可调用对象。

请注意,内置名称有一个单独的约定:大多数内置 名称是单个单词(或两个单词一起运行),带有 CapWords 约定仅用于异常名称和内置常量。

类型变量名

PEP 484 中引入的类型变量的名称通常应使用 CapWords 首选短名称:、、。建议添加 后缀或用于声明协变的变量 或相应的逆变行为:TAnyStrNum_co_contra

from typing import TypeVarVT_co = TypeVar('VT_co', covariant=True)KT_contra = TypeVar('KT_contra', contravariant=True)

异常名称

因为异常应该是类,所以类命名约定 适用于此处。但是,您应该在 异常名称(如果异常实际上是错误)。

全局变量名称

(我们希望这些变量适合在一个模块内使用 仅。这些约定与函数的约定大致相同。

设计用于 via 的模块应使用 防止导出全局变量的机制,或使用 在此类全局变量前面加上下划线的旧约定( 您可能需要指示这些全局变量是“模块 非公开“)。from M import *__all__

函数和变量名称

函数名称应为小写,单词之间用 必要时下划线以提高可读性。

变量名称遵循与函数名称相同的约定。

mixedCase 仅在已经是 主流风格(例如 threading.py),保留向后 兼容性。

函数和方法参数

始终用于实例方法的第一个参数。self

始终用于类方法的第一个参数。cls

如果函数参数的名称与保留关键字冲突,则它是 通常最好附加一个尾随下划线,而不是 使用缩写或拼写损坏。因此更好 比。(也许更好的方法是通过使用 同义词。class_clss

方法名称和实例变量

使用函数命名规则:小写,单词之间用 必要时下划线以提高可读性。

仅对非公共方法和实例使用一个前导下划线 变量。

为避免名称与子类冲突,请使用两个前导下划线 调用 Python 的名称修改规则。

Python 用类名来破坏这些名称:如果类 Foo 有一个 属性名称 ,则无法通过 访问。(一个 坚持使用的用户仍可以通过调用 .) 来获得访问权限。 通常,双前导下划线只能用于避免 名称与设计为子类的类中的属性冲突。__aFoo.__aFoo._Foo__a

注意:关于__names的使用存在一些争议(见下文)。

常数

常量通常在模块级别定义,并写入 用下划线分隔单词的大写字母。示例包括 和 。MAX_OVERFLOWTOTAL

针对继承进行设计

始终决定类的方法和实例变量是否 (统称为:“属性”)应为公共或非公共。如果在 有疑问,选择非公开;晚点公开比公开更容易 将公共属性设为非公共属性。

公共属性是那些你期望与你的不相关的客户端的属性 类使用,并承诺避免向后不兼容 变化。非公共属性是那些不打算公开的属性 由第三方使用;您不保证非公开 属性不会更改,甚至不会被删除。

我们在这里不使用术语“私有”,因为没有属性是真正的 Python 中的私有(通常没有不必要的工作量)。

另一类属性是属于 “子类 API”(在其他语言中通常称为“受保护”)。一些 类被设计为继承、扩展或修改 类行为的各个方面。在设计这样的类时,将 关心对哪些属性是公开的做出明确的决定, 哪些是子类 API 的一部分,哪些是真正唯一的 由基类使用。

考虑到这一点,以下是 Pythonic 指南:

公共属性不应有前导下划线。

如果您的公共属性名称与保留关键字冲突, 在属性名称后附加一个尾随下划线。这是 比缩写或损坏的拼写更可取。(但是, 尽管有此规则,但“cls”是任何 变量或参数,尤其是 类方法的第一个参数。

注 1:有关类方法,请参阅上面的参数名称建议。

对于简单的公共数据属性,最好只公开 属性名称,没有复杂的访问器/变值器方法。保持 考虑到 Python 为未来的增强提供了一条简单的途径, 如果你发现一个简单的数据属性需要增长 功能行为。在这种情况下,请使用属性隐藏 简单数据属性访问背后的功能实现 语法。

注1:尽量保持功能行为无副作用, 尽管缓存等副作用通常没问题。

注 2:避免使用计算成本高昂的属性 操作;属性表示法使调用方相信 访问(相对)便宜。

如果您的类旨在被子类化,并且您具有属性 您不希望使用子类,请考虑将它们命名为 双前导下划线,无尾随下划线。这 调用 Python 的名称修改算法,其中 class 被分解到属性名称中。这有助于避免 属性名称冲突应无意中包含子类 具有相同名称的属性。

注 1:请注意,在 mangled 中仅使用简单的类名 name,因此如果子类同时选择相同的类名和属性 name,您仍然可以获得名称冲突。

注2:名称篡改可以使某些用途,如调试和,不太方便。然而,这个名字的篡改 该算法有据可查,易于手动执行。__getattr__()

注3:不是每个人都喜欢乱名。尝试平衡 需要避免意外的名称冲突,潜在用途 高级呼叫者。

公共接口和内部接口

任何向后兼容性保证仅适用于公共接口。 因此,重要的是用户能够清楚地区分 在公共接口和内部接口之间。

记录的接口被视为公共接口,除非文档 显式声明它们是临时接口或内部接口豁免 从通常的向后兼容性保证。所有无证件 接口应假定为内部接口。

为了更好地支持自省,模块应该显式声明 在其公共 API 中使用该属性的名称。设置为空列表表示该模块没有公共 API。__all____all__

即使设置得当,内部接口(包、 modules、classes、functions、attributes 或其他名称)仍应为 以单个前导下划线为前缀。__all__

接口也被视为内部接口(如果有包含命名空间) (包、模块或类)被认为是内部的。

导入的名称应始终被视为实现详细信息。 其他模块不得依赖于对此类导入名称的间接访问 除非它们是包含模块的显式记录部分 API,例如公开 子模块的功能。os.path__init__

编程建议

代码的编写方式不应不利于他人 Python 的实现(PyPy、Jython、IronPython、Cython、Psyco、 等等)。

例如,不要依赖 CPython 的高效实现 格式为 或 的语句的就地字符串连接。即使在 CPython 中,这种优化也是脆弱的(它 仅适用于某些类型),并且在实现中根本不存在 不使用 refcounting。在性能敏感部件中 库,应改用表单。这将 确保在线性时间内跨各种 实现。a += ba = a + b''.join()

与 None 等单例的比较应始终使用 or ,而不是相等运算符。isis not

此外,当你真正想写的时候要小心——例如,在测试一个变量或参数是否 默认值为 None 已设置为其他值。另一个值可能 具有在布尔值中可能为 false 的类型(例如容器) 上下文!if xif x is not None

使用运算符而不是 .虽然两者兼而有之 表达式在功能上是相同的,前者更具可读性 并首选:is notnot ... is

# Correct:if foo is not None:
# Wrong:if not foo is None:

在实现具有丰富比较的排序操作时,它是 最好实现所有六个操作(、、、、、)而不是依赖 在其他代码上仅进行特定的比较。__eq____ne____lt____le____gt____ge__

为了最大程度地减少所涉及的工作量,装饰器提供了一个工具来生成缺失的比较方法。functools.total_ordering()

PEP 207 表示 Python 假设了自反性规则。 因此,解释器可以交换 、 和 ,并且可以交换 和 的参数。和 操作保证使用 运算符和函数使用运算符。但是,最好实现所有六个操作,以便 这种混淆在其他情况下不会出现。y > xx < yy >= xx <= yx == yx != ysort()min()<max()>

始终使用 def 语句,而不是绑定 直接指向标识符的 lambda 表达式:

# Correct:def f(x): return 2*x
# Wrong:f = lambda x: 2*x

第一种形式表示生成的函数对象的名称为 特别是“f”而不是通用的“<lambda>”。这是更多 通常用于回溯和字符串表示。用途 的赋值语句消除了 lambda 的唯一好处 表达式可以提供显式的 def 语句(即它可以 嵌入到更大的表达式中)

派生异常来自 而不是 。 直接继承是为异常保留的 抓住它们几乎总是错误的做法。ExceptionBaseExceptionBaseException

根据捕获异常的代码可能需要的区别而不是位置来设计异常层次结构 提出例外情况的地方。旨在回答问题 “出了什么问题?”,而不是仅仅陈述 “出现问题”(请参阅 PEP 3151 以获取本课的示例 为内置异常层次结构学习)

类命名约定在这里适用,但您应该添加 后缀“Error”添加到异常类中,如果异常是 错误。用于非本地流控制的非错误异常 或其他形式的信号不需要特殊的后缀。

适当地使用异常链接。 应该用于指示显式替换,而不会丢失 原始回溯。raise X from Y

当有意替换内部异常 (using ) 时,请确保将相关详细信息转移到新的 异常(例如在转换时保留属性名称 KeyError 更改为 AttributeError,或嵌入原始文本 新异常消息中的异常)。raise X from None

捕获异常时,请随时提及特定异常 可能,而不是使用裸子句:except:

try:
    import platform_specific_moduleexcept ImportError:
    platform_specific_module = None

裸子句将捕获 SystemExit 和 KeyboardInterrupt 异常,使中断 使用 Control-C 编程,并可以掩盖其他问题。如果你 想要捕获所有表示程序错误的异常,请使用 (bare except 等效于 )。except:except Exception:except BaseException:

一个好的经验法则是将裸露的“除外”子句的使用限制为两个 例:

如果异常处理程序将打印出来或记录 追踪;至少用户会意识到错误有 发生。

如果代码需要做一些清理工作,但随后让 异常向上传播。 可能是处理这种情况的更好方法。raisetry...finally

捕获操作系统错误时,首选显式异常 在 Python 3.3 中引入的层次结构,而不是对值的自省。errno

此外,对于所有 try/except 子句,请限制子句 到所需的绝对最小代码量。同样,这个 避免屏蔽错误:try

# Correct:try:
    value = collection[key]except KeyError:
    return key_not_found(key)else:
    return handle_value(value)
# Wrong:try:
    # Too broad!
    return handle_value(collection[key])except KeyError:
    # Will also catch KeyError raised by handle_value()
    return key_not_found(key)

资源是特定代码段的本地资源时,请使用语句来确保及时可靠地清理该资源 使用后。try/finally 语句也是可以接受的。with

上下文管理器应通过单独的函数或方法调用 每当他们做一些事情而不是获取和释放资源时:

# Correct:with conn.begin_transaction():
    do_stuff_in_transaction(conn)
# Wrong:with conn:
    do_stuff_in_transaction(conn)

后一个示例没有提供任何信息来指示 和 方法正在做其他事情 而不是在交易后关闭连接。明确是 在这种情况下很重要。__enter____exit__

在返回语句中保持一致。所有 return 语句 函数应该返回一个表达式,或者它们都不应该返回。如果 任何 return 语句都返回一个表达式,任何 return 语句 如果未返回任何值,则应显式将其声明为 ,并且显式返回语句应出现在 函数结束(如果可访问):return None

# Correct:def foo(x):
    if x >= 0:
        return math.sqrt(x)
    else:
        return Nonedef bar(x):
    if x < 0:
        return None
    return math.sqrt(x)
# Wrong:def foo(x):
    if x >= 0:
        return math.sqrt(x)def bar(x):
    if x < 0:
        return
    return math.sqrt(x)

使用 和 代替字符串 切片以检查前缀或后缀。''.startswith()''.endswith()

startswith() 和 endswith() 更简洁,更不容易出错:

# Correct:if foo.startswith('bar'):
# Wrong:if foo[:3] == 'bar':

对象类型比较应始终使用 isinstance() 而不是 直接比较类型:

# Correct:if isinstance(obj, int):
# Wrong:if type(obj) is type(1):

对于序列(字符串、列表、元组),请使用 empty 序列为 false:

# Correct:if not seq:if seq:
# Wrong:if len(seq):if not len(seq):

不要编写依赖于显著尾随的字符串文本 空白。这种尾随空格在视觉上是无法区分的 一些编辑(或最近的 reindent.py)会修剪它们。

不要使用以下命令将布尔值与 True 或 False 进行比较:==

# Correct:if greeting:
# Wrong:if greeting == True:

更 糟:

# Wrong:if greeting is True:

使用 flow control 语句 // 在 a 的 finally 套件中,其中 flow control 声明会跳出最后的套房,是不鼓励的。这 是因为此类语句将隐式取消任何活动异常 这是通过 FINALLY 套件传播的:returnbreakcontinuetry...finally

# Wrong:def foo():
    try:
        1 / 0
    finally:
        return 42


函数注解

随着 PEP 484 的接受,功能的样式规则 批注已更改。

函数注解应使用 PEP 484 语法(有一些 上一节中批注的格式建议)。

建议的注释样式试验 以前在此 PEP 中不再被鼓励。

但是,在 stdlib 之外,现在鼓励在 PEP 484 规则范围内进行实验。例如,标记大型第三方 具有 PEP 484 样式类型注释的库或应用程序, 回顾添加这些注释是多么容易,并观察 它们的存在是否增加了代码的可理解性。

Python 标准库在采用此类 注解,但允许将它们用于新代码和 big 重构。

对于想要以不同方式使用函数注解的代码 建议在表格中发表评论:

# type: ignore

靠近文件顶部;这会告诉类型检查器忽略所有 附注。(更细粒度的禁用投诉的方法 类型检查器可以在 PEP 484 中找到。

与 linter 一样,类型检查器是可选的独立工具。蟒 默认情况下,解释器不应由于类型而发出任何消息 检查,并且不应根据注释更改其行为。

不想使用类型检查器的用户可以自由地忽略它们。 但是,预计第三方库包的用户 可能希望对这些包运行类型检查器。为此,PEP 484 建议使用存根文件:读取的 .pyi 文件 通过类型检查器优先选择相应的.py文件。 存根文件可以与库一起分发,也可以单独分发(使用 库作者的许可)通过 Typeshed 存储库 [5]。

变量注释

PEP 526 引入了变量注释。对他们的风格建议是 与上面描述的函数注解类似:

模块级变量、类和实例变量的注释, 局部变量在冒号后应有一个空格。

冒号前不应有空格。

如果赋值有右侧,则相等符号应有 两边正好一个空格:

# Correct:code: intclass Point:
    coords: Tuple[int, int]
    label: str = '<unknown>'
# Wrong:code:int  # No space after coloncode : int  # Space before colonclass Test:
    result: int=0  # No spaces around equality sign

尽管 Python 3.6 接受 PEP 526,但变量注解 语法是所有版本的 Python 上存根文件的首选语法 (有关详细信息,请参见 PEP 484)。

脚注

悬挂缩进是一种排版样式,其中所有 段落中的行是缩进的,但第一行除外。在 Python 的上下文,该术语用于描述一种风格,其中 括号语句的左括号是最后一个 行的非空格字符,后续行为 缩进,直到右括号。

引用

Barry 的 GNU Mailman 风格指南 https://www.jo6.cn/post/13.html

版权

本文档已置于公有领域。


The End 微信扫一扫

文章声明:以上内容(如有图片或视频在内)除非注明,否则均为腾龙猫勺儿原创文章,转载或复制请以超链接形式并注明出处。

本文作者:猫勺本文链接:https://www.jo6.cn/post/12.html

上一篇 下一篇

相关阅读

发表评论

访客 访客
快捷回复: 表情:
评论列表 (暂无评论,282人围观)

还没有评论,来说两句吧...

取消
微信二维码
微信二维码
支付宝二维码