转载自https://learnku.com/docs/styleofcode/PEP_8/7084

放在这里纯属自己看着方便

介绍

本文档为包含主要 Python 发行版中的标准库的 Python 代码提供了编码约定。请参阅随附的信息性 PEP 描述样式指南,以了解 Python 的 C 实现中的 C 代码。

本文档和 [PEP257](/dev/peps /pep-0257)(Docstring 公约) 改编自 Guido 最初的 Python 样式指南文章,并引用了 Barry 样式指南。

此样式指南会随着时间的推移而演变,因为会确定其他惯例,而过去的惯例会因语言本身的变化而过时。

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

A Foolish Consistency is the Hobgoblin of Little Minds

圭多岛 (Guido) 的主要见解之一是读取的代码通常比编写的要多得多。此处提供的指南旨在提高代码的可读性并使其在 Python 代码的整个光谱范围内保持一致。如 [PEP 20](/dev/peps /pep-0020) 所述, "Readability counts"。

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

但是,请知道何时不一致 - 有时不适用样式指南的建议。如有疑问,请使用最佳判断。查看其他示例并确定最佳外观。而且不要犹豫去问!

特别是:请勿违反向后兼容性以仅符合本 PEP!

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

  1. 应用指南会使代码变得不那么可读,甚至对于被用来阅读遵循此 PEP 的代码的人来说也是如此。
  2. 与周围的代码保持一致 (也可能是出于历史原因)- 尽管这也是清理其他人 (真正的 XP 风格) 的机会。
  3. 因为所提出的代码早于指南的介绍,并且没有其他理由修改该代码。
  4. 需要与旧版本的 Python 保持兼容的代码时,该旧版本不支持样式指南建议的功能。

代码布局

缩进

每个缩进使用四个空格

延续线应使用 Python 的隐式线在括号,括号和花括号内垂直对齐,或使用 hanging indent 对齐的元素。使用悬挂式压痕时,应考虑以下事项;在第一行上不应有任何争论,并应使用进一步的缩进以清楚地区分自己作为延续行。

正确:

# 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)

错误:

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

缩进的带有选项卡和空格的混合的 Python 2 代码应转换为仅使用空格。

当使用 - t 选项调用 Python 2 命令行解释器时,会发出关于非法混合制表符和空格的代码的警告。使用 - tt 时,这些警告会变为错误。强烈建议使用这些选项!

最大线长

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

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

限制所需的编辑器窗口宽度使其可以并排打开多个文件,并且在使用代码检查工具时可以很好地工作,这些工具在列中显示两个版本。

大多数工具中的默认包装都破坏了代码的可视化结构,使其难以理解。选择了限制,以免在编辑器中将窗口宽度设置为 80,即使在包装线时将工具在最后一列中放置了标志符号也是如此。某些基于 Web 的工具可能根本无法提供动态行包装。

有些团队强烈建议使用更长的线路。对于完全或主要由可以就此问题达成协议的团队维护的代码,可以将行长限制增加到 99 个字符,但前提是注释和文档字符串仍保留为 72 个字符。

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

首选的长行包装方式是在括号,括号和花括号内使用 Python 的隐含的行连续性。可以通过将括号中的表达式换行来将长行折断成多行。这些参数应优先用于反斜线以继续生产线。

有时反斜杠可能仍然合适。例如,长时间的多个陈述不能使用隐式连续,因此反斜杠是可以接受的:


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 语句。)

另一种情况是带有断言。

确保适当地缩进连续行。

二进制运算符之前或之后应该断线吗?

对于数十年的建议,在二进制运算符之后会中断。但是,这可能会以两种方式损害可读性:操作员倾向于分散在屏幕上的不同列中,并且每个操作员都从其操作员和先前的行上移开了。在这里,眼睛需要做一些额外的工作来说明增加了哪些项目并减去了哪些项目:


#否:操作员位于远离他们的操作员的地方

income = (gross_wages +

          taxable_interest +

          (dividends - qualified_dividends) -

          ira_deduction -

          student_loan_interest)

为了解决此可读性问题,数学家及其发布者遵循了相反的惯例。唐纳德・克努思 (Donald Knuth) 在他的_计算机和排字_系列中解释了传统规则:尽管在二进制运算和关系之后的段落中,公式始终会中断,但在 [二进制 10] 之前显示的公式总是会中断。

遵循数学上的传统方法通常会得到更易读的代码:


#是:易于将操作员与操作数匹配

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 数据包含在字符串文字中。

对于 Python 3.0 及更高版本,对标准库规定了以下策略 (请参阅 PEP 3131 ):Python 标准库中的所有标识符必须使用纯 ASCII 标识符,并且必须使用 ASCII 标识符在可行的情况下使用英语单词 (在许多情况下使用英文而不是缩写和技术术语)。此外,字符串文字和注释也必须使用 ASCII 。唯一的例外是 (a) 测试非 ASCII 功能的测试案例和 (b) 作者的姓名。作者的名称不是基于拉丁字母 (latin-1,ISO / IEC 8859-1 字符集) 的,因此必须在此字符集中提供其名称的音译。

鼓励采用全球审计的开源项目采用相似的策略。

Imports

  • 通常应在单独的行上进行导入:

Yes: import os

import sys

No:  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

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

隐式相对导入不应_使用_并已在 Python 3 中删除。

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

from myclass import MyClass

from foo.bar.yourclass import YourClass

如果此拼写引起本地名称冲突,则将其拼写为:

import myclass

import foo.bar.yourclass

并使用 “myclass.MyClass” 和 “ foo.bar.yourclass.YourClass”。

应当避免通配符导入 (从导入),因为它们不清楚名称空间中存在哪些名称,这会混淆阅读器和许多自动化工具。对于通配符导入,存在一种可辩护的用例,该用例将作为公共 API 的一部分重新发布内部接口 (例如,重写带有定义的纯 Python 实现的接口,该定义将由执行者和可选的加速器来定义)。提前未知)。

这样重新发布名称时,仍然适用以下关于公共和内部接口的准则。

模块级别地下名称

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

字符串语录

在 Python 中,单引号字符串和双引号字符串是相同的。此 PEP 对此不做任何建议。选择一个规则并坚持下去。当字符串包含单引号或双引号字符时,请使用其他字符串避免在字符串中加反斜杠。它提高了可读性。

对于三重引用的字符串,请始终使用双引号字符以与 [PEP257](/dev/peps /pep-0257) 中的 d ocstring 约定一致。

表达式和语句中的空格

Pet Peeves

避免在以下情况下出现过多的空白:

  • 请立即放在括号,括号或括号内。
Yes: spam(ham[1], {eggs: 2})

No:  spam( ham[ 1 ], { eggs: 2 } )

*  在逗号之间和紧跟的括号之间。

Yes: foo = (0,)

No:  bar = (0, )

*  立即在逗号,分号或冒号之前:

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]

*   立即在打开函数调用参数列表的圆括号之前:

Yes: spam(1)、
No:  spam (1)

*  立即在开始括号或切片的开放括号之前:

Yes: dct['key'] = lst[index]、
No:  dct ['key'] = lst [index]

*  分配者 (或其他) 运算符周围的空格要多于一个空间,以使其与另一个对齐。

Yes:

x = 1、
y = 2、
long_variable = 3

No:

x             = 1、
y             = 2、
long_variable = 3

 其他建议

* 避免在任何地方拖空白。由于它通常是不可见的,因此可能会造成混淆:空格后面的反斜杠和换行符不视为行继续标记。一些编辑者不保留它,并且许多项目 (例如 CPython 本身) 已经预先提交了拒绝它的钩子。

*  始终围绕这些二进制运算符在两侧具有单个空格:赋值 (=),扩展赋值 (+ =,-= 等),比较 (==,<,>,!=,<>,<=,> =,in,not in,is,not),Booleans (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)

*  功能注释应使用分号的常规规则,并始终在 -> 箭头周围留有空格。 (有关功能注解的更多信息,请参见下面的 (See Function Annotations

    Yes:


        def munge(input: AnyStr): ...

        def munge() -> PosInt: ...

    No:


        def munge(input:AnyStr): ...

        def munge()->PosInt: ...

*  请勿在用于指示关键字参数的符号或用于指示未标注功能参数的默认值的符号附近使用空格。

    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:


        if foo == 'blah':

            do_blah_thing()

        do_one()

        do_two()

        do_three()

    Rather not:


        if foo == 'blah': do_blah_thing()

        do_one(); do_two(); do_three()

*  有时可以在同一条线上的一个小机构中 / 如果 / 同时使用一个小型机构,则永远不要对多条款语句执行此操作。还要避免折叠这样的长线!

    Rather not:


        if foo == 'blah': do_blah_thing()

        for x in lst: total += x

        while t < 10: t = delay()

    Definitely not:


        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 中具有用于打印语句的语义)。为了清楚起见,建议将 (技术冗余) 括号内的内容括起来。

Yes:


    FILES = ('setup.cfg',)

OK, but confusing:


    FILES = 'setup.cfg',

当冗余冗余时,在使用版本控制系统时,在列出值,参数或导入的项目时,它们通常会有所帮助。该模式是自己在一条线上输入每个值 (等),并始终添加尾随逗号,并在下一行上添加右括号 / 括号 / 括号。但是,在与闭合分隔符相同的行上没有尾随逗号是有意义的 (单根元组以上情况除外)。

Yes:


    FILES = [

        'setup.cfg',

        'tox.ini',

        ]

    initialize(FILES,

               error=True,

               )

No:


    FILES = ['setup.cfg', 'tox.ini',]

    initialize(FILES, error=True,)

 注释

与代码相反的注释比没有注释更糟糕。更改代码时,请始终优先考虑最新评论!

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

块注释通常由完整的句子构成的一个或多个段落组成,每个句子在一段时间内结束。

句子结束后,您应在多个句子注释中使用两个空格,但最终句子除外。

书写英语时,请紧跟 Strunk 和 White。

来自非英语国家的 Python 编码器:请用英语写下您的评论,除非您有 120%的权限,否则不会说这种语言的人永远不会阅读该代码。

 块注释

块注释通常适用于遵循它们的某些 (或所有) 代码,并且缩进到与该代码相同的级别。每一行注释都以 #和单个空格开头 (除非注释内有缩进文本)。

注释中的段落由包含单个 #的行分隔。 

 内联评论

分别使用内联注释。

内联注释是与语句相同的注释。内联注释应与声明至少相隔两个空格。它们应该以 #和单个空格开头。

内联注释是不必要的,并且如果它们声明是显而易见的,则可以分散注意力。不要这样做:


    x = x + 1                 # Increment x

But sometimes, this is useful:


   x = x + 1                 # Compensate for border

Documentation Strings


在 PEP 257 中,用于书写良好文档字符串的约定 (也称为 “ docstring”) 是不朽的。

* 为所有公共模块,函数,类和方法编写文档字符串。 Docstrings 对于非公开方法不是必需的,但是您应该有一条注释来描述该方法的用途。此注释应出现在 def 行之后。

*   PEP 257 描述了良好的文档字符串约定。请注意,最重要的是,以多行文档字符串结尾的 “”“应单独放在一行上:


        """Return a foobang

        Optional plotz says to frobnicate the bizbaz first.

        """

*  对于一个衬里文档字符串,请保持在同一行上的关闭 """

命名公约

Python 库的命名约定有点麻烦,因此我们永远都不会使其完全一致 - 尽管如此,这是当前推荐的命名标准。应该按照这些标准编写新的模块和程序包 (包括第三方框架),但是在现有库具有不同风格的情况下,最好使用内部一致性。

Overriding Principle 优先原则


API 的公共部分对用户可见的名称应遵循反映用法而不是实现的惯例。

Descriptive: Naming Styles 描述性:命名样式


有很多不同的命名样式。它可以独立于所使用的名称来识别所使用的命名方式。

常见的命名方式有以下几种:

* b (单个小写字母)

* B (单个大写字母)

* 小写

* lower_case_with_underscores

* 大写

* UPPER_CASE_WITH_UNDERSCORES

* 大写字母 (或 CapWords 或 CamelCase - 之所以命名,是因为其字母 [4] 看起来很笨拙)。有时也称为 StudlyCaps。

注意:在 CapWords 中使用首字母缩写词时,请全部使用首字母缩写词的大写字母。因此,HTTPServerError 比 HttpServerError 要好。

* 混合大小写 (与首字母大写字母不同!

* Capitalized_Words_With_Underscores (难看!)

还有一种使用简短的唯一前缀将组相关名称组合在一起的样式。在 Python 中并没有使用太多,但是为了完整起见提到了它。例如,os.stat () 函数返回一个元组,该元组在传统上具有诸如 st_mode,st_size,st_mtime 之类的名称。 (这是为了强调与 POSIX 系统调用结构的字段的对应关系,这有助于程序员熟悉该结构。)

X11 库使用前导 X 表示其所有公共功能。在 Python 中,这种样式通常被认为是不必要的,因为属性和方法的名称都以对象为前缀,功能名称以模块名作为前缀。

此外,还认可了以下使用前导或尾随下划线的特殊形式 (通常可以与任何情况下的惯例结合使用):

 _single_leading_underscore:弱的 “内部使用” 指示符。例如。从 M 导入不会导入名称以下划线开头的对象。

* single_trailingunderscore:用于召开会议,以避免与 Python 关键字发生冲突,例如

Tkinter.Toplevel (母版,class _ ='ClassName')

double_leading_underscore:命名类属性时,调用名称修改 (在 FooBar 内部, boo 成为_FooBar__boo; 请参见下文)。

double_leading_and_trailing_underscore:存在于用户控制的命名空间中的 “魔术” 对象或属性。例如。 init  import 或 file。切勿发明此类名称;只能按记录使用它们。

说明性:命名公约


 要避免的名称

请勿将 'l'(小写字母),'O'(大写字母 oh) 或 'I'(大写字母的眼睛) 用作单个字符变量名称。

在某些字体中,这些字符与数字一和零是无法区分的。尝试使用 'l' 时,请改为使用 'L'。

 ASCII 兼容性

标准库中使用的标识符必须与 PEP 3131 的策略部分中所述的 ASCII 兼容。 / peps / pep-3131)。

 包装和模块名称

模块应使用短小写全名。如果可以提高模块的可读性,则可以在模块名称中使用下划线。尽管不鼓励使用下划线,但 Python 软件包也应使用短小写的全称。

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

 类名

类别名称通常应使用 CapWords 惯例。

可以代替功能的命名约定,而应在已记录接口且主要以可调用方式使用的情况下使用。

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

 类型变量名称

在 PEP484 中引入的类型变量的名称通常应使用 CapWords,最好使用短名称:T,AnyStr,Num。建议将_co 或_contra 后缀添加到用于声明协变量或相反变量行为的变量中:


        from typing import TypeVar

        VT_co = TypeVar('VT_co', covariant=True)

        KT_contra = TypeVar('KT_contra', contravariant=True)

 [异常名称]

由于应将异常作为类,因此在此适用类命名约定。但是,您应该在例外名称上使用后缀 `` 错误 ''(如果例外实际上是错误的)。

 [全局变量名称]

希望这些变量仅可在一个模块内使用。) 有关公约的惯例与功能相同。

设计用于通过 M 导入 * 使用的模块应使用 all 机制以防止导出全局变量,或使用带有较大下划线前缀的此类常规全局变量 (您可能会想在全球范围内使用此下划线)。 ”)。

 函数和变量名称

功能名称应使用小写字母,并用下划线将单词分开,以提高可读性。

变量名称遵循与功能名称相同的约定。

只允许在已经存在流行样式 (例如 threading.py) 的上下文中保留 MixedCase,以保持向后兼容。

 功能和方法参数

对于实例方法,请始终将自己用作第一个参数。

对于第一个参数,请始终使用 cls 对方法进行分类。

如果功能参数的名称与保留的关键字发生冲突,通常最好在其后加上一个下划线,而不是使用缩写或拼写错误。因此 class_比 clss 要好。 (也许最好通过使用同义词来避免此类冲突。)

 方法名称和实例变量

使用功能命名规则:为提高可读性,必须使用小写并用下划线分隔单词。

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

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

Python 将这些名称与类名称混合在一起:如果 Foo 类具有名为 a 的属性,则 Foo . a 无法访问它。 (始终存在的用户可以通过调用 Foo._Foo__a 来获得访问权限。) 通常,应仅使用双引号下划线来避免名称冲突,并且应将属性归类为子类。

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

 常量

常量通常在模块级别定义,并在所有大写字母中写有下划线,并用下划线隔开。示例包括 MAX_OVERFLOW 和 TOTAL。

 继承设计

始终确定类别的方法和实例变量 (统称为 `` 属性 '') 应该是公开的还是不公开的。如果有疑问,请选择非公开;公开发布的时间要比公开发布非公开的时间要容易。

公开属性是指您希​​望您的不相关客户端使用的类别,并承诺避免向后不兼容的更改。非公开属性是指不打算由第三方使用的属性;您不保证非公开属性不会更改或被删除。

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

属于 “子类 API”(通常在其他语言中称为 “受保护”) 的一部分的另一类属性。设计某些类是为了继承,扩展或修改类行为的方面。在设计此类时,请注意明确决定哪些属性是公开的,属于子类 API 的一部分,以及哪些仅由您的基础类真正使用。

考虑到这一点,以下是 Python 准则:

* 公开属性应没有领先的下划线。

* 如果您的公开属性名称与保留的关键字冲突,则在您的属性名称后附加一个单下划线。这是缩写或损坏的拼写的最佳选择。 (但是,尽管有此规则,对于任何已知为一类,尤其是一类方法的变量或参数,'cls' 是首选的拼写。)

注意 1:有关类方法,请参见上文的参数名称建议。

* 对于简单的公共数据属性,最好公开属性名称,而没有复杂的访问者 / 更改者方法。请记住,Python 为将来的增强提供了一条简单的途径,您应该发现需要简单的数据属性来增长功能行为。在这种情况下,请使用属性来隐藏简单数据属性访问语法后面的功能实现。

注意 1:仅适用于新型类的属性。

注 2:尝试保持功能上无副作用,尽管通常情况下缓存等副作用很小。

注 3:避免使用属性进行计算上昂贵的操作;属性标记使呼叫者相信访问 (相对) 便宜。

* 如果打算将您的类别归为子类别,并且您已归因于您不想使用子类别,请考虑使用双前导下划线和无尾划线来命名它们。这会调用 Python 的名称处理算法,其中将类别的名称与属性名称混合在一起。这有助于避免属性名称冲突应被子类意外地包含具有相同名称的属性。

注 1:请注意,只有一个简单的类名可以在混合名称中使用,如果一个子类选择了相同的类名和属性名称,您仍可以得到名称冲突。

注 2:名称修改可能会带来某些不便,例如调试和 getattr ()。但是,名称处理算法已被记录在案,并且易于手动执行。

注意 3:不是每个人都喜欢名字修改。尝试平衡高级呼叫者可能使用的名称冲突,以免造成意外冲突。

公共和内部接口


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

除非书面文件明确声明它们是临时的或内部的接口,否则它们均应视为公开的接口,并且不受通常的向后兼容性的保证。所有未公开的接口都应假定为内部接口。

为了更好地支持自检,应使用 all 属性在其公共 API 中明确声明模块名称。将 all 设置为一个空列表表示该模块没有公共 API。

即使正确设置了 all,内部接口 (软件包,模块,类,功能,属性或其他名称) 仍应使用单个领先的下划线作为前缀。

如果内部考虑任何包含命名空间 (包,模块或类) 的接口,则内部也要考虑该接口。

始终应考虑导入的名称的实施细节。其他模块必须不间接访问此类导入的名称,除非它们是包含模块的 API 的明确记录的一部分,例如 os.path 或具有功能性的软件包的 init 模块。

编程建议

* 代码的编写方式不应影响 Python 的其他实现 (PyPy、Jython、IronPython、Cython、Psyco 等等)。

例如,不要依赖于 CPython 对 a+=b 或 a=a+b 形式的语句的就地字符串连接的有效实现。这种优化即使在 CPython 中也很脆弱 (它只适用于某些类型),而且在不使用 refcounting 的实现中根本不存在库的非活动部分。应改用 “”join () 形式。这将确保在各种实现中以线性时间进行连接。

* 与 singleton 的比较,例如不应始终使用 is 或 is 或 is,而不要使用相等运算符。

此外,当您真正的意思是如果 x 不是 None 时,请注意不要写 if x—— 例如,当测试默认为 None 的变量或参数是否设置为其他值时。其他值可能具有在布尔上下文中可能为 false 的类型 (如容器)!

*Use 不是运算符,而不是 not…is。虽然两个表达式在功能上是相同的,前者更具可读性和优先性。

Yes:

           if foo is not None:

No:

           if not foo is None:

* 在实现具有丰富比较的排序操作时,最好实现所有六个操作 (<uu eqò,uu neò,不是依赖其他代码来进行特定的比较,而是尽量减少所需的工作量,“工具” 生成缺失的比较方法。

α[PEP] 207 (/DEV/PEPS/PEP-0207) 表明:反自反性的规则是由 Python 假设的。{函数工具。可以交换 x==y 和 x 的参数!=y。sort () 和 min () 操作保证使用 < 运算符,max () 函数使用 > 运算符。但是,最好实现这六个操作,以免在其他上下文中出现混淆。

* 始终使用 def 语句,而不是直接将 lambda 表达式绑定到标识符的赋值语句。

Yes:

          def f(x): return 2*x

No:

          f = lambda x: 2*x

第一种形式表示结果函数对象的名称是 “f”,而不是泛型的 “”。这通常对于回溯和字符串表示更有用。使用赋值语句可以消除 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 转换为 attributerror 时保留属性名,或在新异常消息中嵌入原始异常的文本)。

* 在 Python 2 中引发异常时,使用 raise ValueError ('message') 而不是旧的表单 raise ValueError,'message'。

后一个表单不是合法的 Python 3 语法。

paren using 表单还意味着当异常参数很长或包含字符串格式时,由于包含圆括号,您不需要使用换行符。

* 捕获异常时,尽可能提及特定的异常,而不是使用空的 except: 子句:


        try:

            import platform_specific_module

        except ImportError:

            platform_specific_module = None

一个好的经验法则是将 bare “Exception” 子句的使用限制为两种情况:

1. 如果异常处理程序将打印或记录回溯;至少用户会意识到发生了一个错误。

2. 如果代码需要进行一些清理工作,但随后会让异常随着 raise 向上传播。尝试… 最后是处理这种情况的更好方法。

* 将捕获的异常绑定到名称时,首选 Python 2.6 中添加的显式名称绑定语法:

        try:

            process_data()

        except Exception as exc:

            raise DataProcessingFailedError(str(exc))

 这是 Python 3 中唯一支持的语法,并且避免了与旧的基于逗号的语法相关联的歧义问题。

* 在捕获操作系统错误时,更喜欢 Python 3.3 中引入的显式异常层次结构,而不是 errno 值的内省。

* 此外,对于所有 try/except 子句,将 try 子句限制为所需代码的绝对最小数量。再次,这避免了掩蔽错误。

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 语句。

  • 上下文管理器在执行除获取和释放资源以外的操作时,应通过单独的函数或方法调用。

Yes:


        with conn.begin_transaction():

            do_stuff_in_transaction(conn)

No:


        with conn:

            do_stuff_in_transaction(conn)

最近的示例没有提供任何信息来表明 ent 和 exit 方法正在执行其他操作,而不是在事务处理后关闭连接。 在这种情况下,明确是很重要的。

*   在返回语句中保持一致。函数中的所有返回语句都应返回表达式,如果任何 return 语句返回表达式,则没有返回值的 return 语句应显式声明为 return none,并且在函数的末尾 (如果可以访问) 应该有显式的返回语句。

Yes:

        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)

No:

        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 版以上的 Python,则需要覆盖此规则。

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

startwith()和 endwith()更干净,出错更少:

    Yes: if foo.startswith('bar'):

    No:  if foo[:3] == 'bar':

*  对象类型比较应始终使用 isinstance()代替直接比较类型。


    Yes: if isinstance(obj, int):

    No:  if type(obj) is type(1):

检查对象是否为字符串时,请切记它也可能是 unicode 字符串! 在 Python 2 中,str 和 unicode 具有通用的基本类,基本字符串,因此您可以执行以下操作:

        if isinstance(obj, basestring):

请注意,在 Python 3 中,不再存在 unicode 和 basestring(仅存在 str),并且一个字节对象不再具有某种类型的 string(它是整数的序列)。

* 对于序列(字符串,列表,元组),请使用空序列为假的事实。

    Yes: if not seq:

            if seq:

    No:  if len(seq):

            if not len(seq):
  • 不要写依赖于重要尾随空白的字符串文字。 这种尾随空白在视觉上是无法区分的,并且一些编辑者(或者最近是 reindent.py)将对其进行修剪。
  • 不要使用 == 将布尔值比较为 True 或 False。
    Yes:   if greeting:

    No:    if greeting == True:

        Worse: if greeting is True:
  • 最后使用流控制语句返回 / 中断 / 继续... 最后,在最终适合的情况下,不建议使用流控制语句跳转。 这是因为此类声明将隐含取消通过最终套件传播的任何活动例外。

No:

        def foo():

            try:

                1 / 0

            finally:

                return 42

 功能注释

随着 PEP484 的接受,功能注释的样式规则也在不断变化。

* 为了向前兼容,Python 3 代码中的功能注释最好使用 PEP484 语法。 (在前一节中有一些关于格式注释的格式建议。)

* 不再鼓励在本 PEP 中以前建议使用带有注释样式的实验。

* 但是,现在鼓励在 stdlib 之外进行 PEP484 规则内的实验。例如,使用 PEP484 样式类型的注释标记大型第三方库或应用程序,查看添加这些注释的难易程度,并观察其可理解性。

* Python 标准库在采用此类注释时应保持保守,但允许新代码和大型重构使用它们。

* 对于想要不同使用功能注释的代码,建议使用以下格式的注释:


#类型:忽略

 在文件顶部附近;这种类型的检查器可忽略所有注释。 (可以在 PEP484 中找到更多来自类型检查器的细粒度投诉。)

* 像 毛毡一样,类型检查器是可选的,独立的工具。默认情况下,Python 解释器不应由于类型检查而发出任何消息,也不应基于注释改变其行为。

* 不想使用类型检查器的用户可以随意忽略它们。但是,预计第三方库软件包的用户可能希望在这些软件包上运行类型检查器。为此,PEP484 建议使用存根文件:.pyi 文件是由类型检查器优先读取的.pyi 文件。存根文件可以通过键入的存储库 [5] 与库一起分发,也可以单独分发 (经库作者的许可)。

* 对于需要向后兼容的代码,可以在注释表格中添加类型注释。请参阅 PEP484[6] 的相关部分。

变量注解

PEP 526 引入了变量注释。对其的样式建议与上述功能注释类似:

* 模块级别变量,类和实例变量以及本地变量的注释应在冒号后留一个空格。

* 在冒号之前应该没有空格。

* 如果分配有正确的一面,则两边的平等标志应完全相同。

*   Yes:


        code: int

        class Point:

            coords: Tuple[int, int]

            label: str = '<unknown>'

*   No:


        code:int  # No space after colon

        code : int  # Space before colon

        class Test:

            result: int=0  # No spaces around equality sign

*  尽管 Python 3.6 接受 PEP526,但变量注释语法是所有版本 Python 上存根文件的首选语法 (请参阅 PEP / eps484

脚注

[7]_悬挂缩进是一种类型设置样式,其中除第一行外,所有段落中的所有行均已缩进。在 Python 的上下文中,该术语用于描述一种样式,其中带括号的语句的开头括号是该行的最后一个非空白字符,其后的行直到该行都被闭合。

参考

[1] PEP 7, C 代码样式指南,van Rossum

[2] 巴里 (Barry) 的 GNU Mailman 风格指南 barry.warsaw.us/software/STYLEGUIDE...

[3] Donald Knuth 的 The TeXBook,第 195 页和 196。

[4] www.wikipedia.com/wiki/CamelCase

[5] Typeshed repo github.com/python/typeshed

[6]  Python 2.7 的建议语法和跨平台代码 www.python.org/dev/peps/pep-0484/#...

版权

本文档已放置在公共域中。

来源: github.com/python/peps/blob/master...