PEP 8-Python代码样式指南

PEP 8-Python代码样式指南

在这里插入图片描述



介绍

本文档给出了Python代码的编码约定,该Python代码包含主Python发行版中的标准库。请参阅随附的信息性PEP,该PEP描述了Python 1的C实现中C代码的样式准则。

本文档和PEP 257(Docstring约定)是从Guido最初的Python样式指南文章中改编而来,并在Barry的样式指南中2作了一些补充。

该样式指南会随着时间的流逝而发展,因为会识别其他约定,而过去的约定会因语言本​​身的更改而变得过时。

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


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

Guido的主要见解之一是代码被读取的次数远多于其编写的次数。此处提供的指南旨在提高代码的可读性,并使其在各种Python代码中保持一致。正如PEP 20所说,“可读性至关重要”。

样式指南是关于一致性的。与该样式指南的一致性很重要。项目内的一致性更重要。一个模块或功能内的一致性是最重要的。

但是,要知道何时会出现不一致的情况-有时,样式指南的建议就不适用。如有疑问,请运用您的最佳判断。查看其他示例,然后确定最适合的示例。不要犹豫,问!

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

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

应用指南时,即使对于那些习惯于阅读遵循此PEP的代码的人,也会使代码的可读性降低。
为了与周围的代码一致(也可能出于历史原因),这也会破坏它(尽管这也是清理别人的混乱的机会(以真正的XP风格))。
由于所讨论的代码早于准则的引入,因此没有其他理由修改该代码。
当代码需要与不支持样式指南建议功能的旧版Python保持兼容时。


代码布局

缩进

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

续行应该使用在括号,方括号和花括号内连接的Python隐式线或使用悬挂式缩进来垂直对齐换行的元素。使用悬挂式凹痕时,应考虑以下几点:第一行不应包含任何参数,并且应使用进一步的缩进形式将其清楚地区分为延续行:

悬挂缩进是一种类型设置样式,其中段落中除第一行外的所有行均进行缩进。在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. 添加4个空格(额外的缩进级别)以将参数与其余参数区分开。
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. 悬挂的缩进*可以*缩进到4个空格以外的其他空格。
foo = long_function_name(
  var_one, var_two,
  var_three, var_four)

如果if-语句的条件部分足够长,可以要求将其写成多行,则值得注意的是,两个字符关键字(即if),一个空格和一个圆括号的组合会产生一个自然的多行条件的后续行的4个空格缩进。这可以产生具有的代码缩进套件嵌套内的一个视觉冲突if语句来,这将也自然地缩进到4位。对于如何(或是否)在视觉上进一步将这些条件行与if语句内的嵌套套件区分开来,PEP不做任何明确的表述。在这种情况下,可接受的选项包括但不限于:

# 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语句的时候不能使用隐式连续,因此可以使用反斜杠:

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

(有关对with-语句的多行缩进的进一步思考,请参见前面关于多行if语句的讨论。)

另一种此类情况是使用assert语句。

确保适当缩进续行。


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

几十年来,推荐的方式是在二进制运算符之后中断。但这会以两种方式损害可读性:运算符趋向于分散在屏幕上的不同列上,并且每个运算符都从其操作数移至上一行。在这里,眼睛必须做额外的工作才能分辨出添加了哪些项目和减去了哪些项目:

# Wrong: 错误:
# operators sit far away from their operands 运算符远离其操作数
income = (gross_wages +
          taxable_interest +
          (dividends - qualified_dividends) -
          ira_deduction -
          student_loan_interest)

为了解决此可读性问题,数学家及其发布者遵循相反的约定。Donald Knuth在他的“计算机和排版”系列中解释了传统规则:“尽管段落中的公式总是在二进制运算和关系之后中断,而显示的公式总是在二进制运算和关系之前中断” 3

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

# Correct: 正确:
# easy to match operators with operands 易于将运算符与操作数匹配
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。唯一的例外是(a)测试非ASCII功能的测试用例,以及(b)作者的姓名。名称不基于拉丁字母(latin-1,ISO / IEC 8859-1字符集)的作者必须在此字符集中提供其姓名的音译。

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


导入

  • 导入通常应在单独的行上:
# Correct:
import os
import sys
# Wrong:
import sys, os

可以这样说:

# Correct:
from subprocess import Popen, PIPE
  • 导入总是放在文件的顶部,紧随任何模块注释和文档字符串之后,以及模块全局变量和常量之前。

    导入应按以下顺序分组:
    1、标准库导入。
    2、相关第三方导入。
    3、本地应用程序/库特定的导入。

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

  • 推荐绝对导入,因为如果导入系统配置不正确(例如,程序包中的目录最终位于sys.path时),则它们通常更具可读性,并且通常表现得更好(或至少会提供更好的错误消息):

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 < module > import *),因为通配符使不清楚名称空间中存在哪些名称,从而使读者和许多自动化工具感到困惑。通配符导入有一个合理的用例,它是将内部接口重新发布为公共API的一部分(例如,用可选加速器模块中的定义覆盖接口的纯Python实现,以及覆盖哪些定义是不知道的)。

    以这种方式重新发布名称时,下面有关公共和内部接口的指导原则仍然适用。


模块级Dunder名称

模块级“dunders”(即带有两个前导下划线和两个尾随下划线的名称)如__all__,__ author __,__version__等应被放置在模块文档字符串之后,但在任何导入语句除了 from__future__imports以外。Python强制要求future-imports必须在模块中出现在除文档字符串以外的任何其他代码之前:

"""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对此不做任何建议。选择一条规则并坚持下去。但是,当字符串包含单引号或双引号字符时,请使用另一个以避免在字符串中使用反斜杠。它提高了可读性。

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


表达式和语句中的空白

宠物皮皮

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

  • 紧靠在括号,方括号或大括号内:
# 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 : : upper]
ham[ : upper]
  • 在函数调用的参数列表的括号之前:
# Correct:
spam(1)
# Wrong:
spam (1)
  • 在开始索引或切片的左括号之前:
# Correct:
dct['key'] = lst[index]
# Wrong:
dct ['key'] = lst [index]
  • 赋值(或其他)运算符周围有多个空格,以使其与另一个对齐:
# Correct:
x = 1
y = 2
long_variable = 3
# Wrong:
x             = 1
y             = 2
long_variable = 3

其他建议

  • 避免在任何地方使用尾随空格。因为它通常是不可见的,所以可能会造成混淆:例如,反斜杠后跟空格和换行符不算作行继续标记。有些编辑器不保留它,许多项目(比如CPython本身)都有拒绝它的预提交挂钩。
  • 始终在两侧用单个空格将这些二进制运算符包围起来:赋值(=),扩展赋值(+ =,-= 等),比较(==,<,>,!=,<>,<=, > =,in,not in,is,is not),布尔值(and, or,not)。
  • 如果使用具有不同优先级的运算符,请考虑在具有最低优先级的运算符周围添加空格。根据您自己的判断;但是,不要使用多个空格,并且在二进制运算符的两边总是有相同数量的空格:
# Correct:
i = i + 1
submitted += 1
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)
# Wrong:
i=i+1
submitted +=1
x = x * 2 - 1
hypot2 = x * x + y * y
c = (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 += x
while 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()

何时使用尾随逗号

尾随逗号通常是可选的,除非它们在生成一个元素的元组时是必需的(在python2中,它们具有print语句的语义)。为清楚起见,建议将后者括在(技术上多余的)括号中:

# 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%的把握不懂你的语言的人永远不会读到你的代码。


块注释

块注释通常适用于其后的一些(或全部)代码,并且缩进到与该代码相同的级别。块注释的每一行都以#和一个空格开头(除非注释中的文本是缩进的)。

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


行内注释

谨慎使用行内注释。

行内注释是与语句在同一行上的注释。行内注释应与该语句至少分隔两个空格。它们应以#和单个空格开头。

行内注释是不必要的,并且如果它们表明明显的内容,则实际上会分散注意力。不要这样做:

x = x + 1                 # Increment x 增量x

但是有时候,这很有用:

x = x + 1                 # Compensate for border 补偿边界

文档字符串

编写好的文档字符串(也称为“docstrings”)的约定在pep257中被永久化。

  • 为所有公共模块,函数,类和方法编写文档字符串。对于非公共方法,文档字符串不是必需的,但是您应该有一条注释,描述该方法的作用。该注释应出现在def行之后。
  • PEP 257描述了良好的文档字符串约定。请注意,最重要的是,结束多行文档字符串的 " " " 应单独位于一行上:
"""Return a foobang

Optional plotz says to frobnicate the bizbaz first.
"""
  • 对于一行 docstrings,请将结束符 " " " 保持在同一行:
"""Return an ex-parrot."""

命名约定

Python库的命名约定有些混乱,因此我们永远都无法做到完全一致-尽管如此,这是当前推荐的命名标准。新的模块和软件包(包括第三方框架)应按照这些标准编写,但是在现有库具有不同样式的情况下,内部一致性是首选。


首要原则

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


描述性:命名样式

有很多不同的命名样式。能够独立于它们的用途来识别正在使用的命名方式。

通常区分以下命名样式:

  • b(单个小写字母)
  • B(单个大写字母)
  • 小写
  • 带下划线的小写字母
  • 大写
  • 带下划线的大写字母
  • 大写的单词(或CapWords大写词或者CamelCase驼峰命名法——因为字母的凹凸而得名4)。有时也称为StudlyCaps。注意:在CapWords中使用首字母缩写词时,请使用首字母缩写词的所有字母大写。因此,HTTPServerError比HttpServerError更好。
  • mixedCase混合情况(与大写单词的首字母小写字符不同!)
  • 带下划线的大写单词(丑陋!)

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

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

此外,还可以识别出以下使用前划线或下划线的特殊形式(通常可以将它们与任何大小写惯例结合使用):

  • _single_leading_underscore:“内部使用”指标较弱。例如,从M import *不会导入名称以下划线开头的对象。
  • single_trailing_underscore_:约定用于避免与Python关键字冲突,例如
tkinter.Toplevel(master, class_='ClassName')
  • __double_leading_underscore:命名类属性时,调用名称修饰(在类FooBar中,__boo变为 _FooBar__boo;请参见下文)。
  • double_leading_and_trailing_underscore:存在于用户控制的命名空间中的“魔术”对象或属性。例如__init__,__ import__或__file__。千万不要发明这样的名字;仅按记录使用它们。

说明性:命名约定

避免使用的名称

切勿将字符“ l”(小写字母el),“ O”(大写字母oh)或“ I”(大写字母eye)用作单个字符变量名称。

在某些字体中,这些字符与数字1和零没有区别。当尝试使用“ l”时,请改用“ L”。


ASCII兼容性

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


软件包和模块名称

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

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


类名

类名通常应使用CapWords约定。

在接口被记录并主要用作可调用函数的情况下,可以代替使用函数的命名约定。

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


类型变量名

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

from typing import TypeVar

VT_co = TypeVar('VT_co', covariant=True)
KT_contra = TypeVar('KT_contra', contravariant=True)
<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

异常名称

因为异常应该是类,所以在这里适用类命名约定。但是,您应该在异常名称上使用后缀“ Error”(如果异常实际上是一个错误)。


全局变量名

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

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


函数和变量名

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

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

仅在已经是主流样式(例如threading.py)的上下文中才允许使用mixedCase,以保持向后兼容性。


函数和方法参数

始终将self作为实例方法的第一个参数。

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

如果函数参数的名称与保留关键字发生冲突,通常最好在末尾附加一个下划线,而不要使用缩写或拼写错误。因此,class_优于clss。(也许更好的办法是使用同义词来避免此类冲突。)


方法名称和实例变量

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

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

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

Python使用类名来修饰这些名称:如果Foo类具有名为__a的属性,则Foo .__ a不能访问它。(坚持的用户仍可以通过调用Foo._Foo__a来获得访问权限。)通常,双引号下划线仅应用于避免名称与设计为子类的类中的属性发生冲突。

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


常数

常量通常在模块级别定义,并以所有大写字母书写,并用下划线分隔单词。示例包括 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等)的方式编写代码。

    例如,对于形式为+ = b 或a = a +
    b的语句,请不要依赖CPython有效地实现就地字符串连接。即使在CPython中,这种优化也是脆弱的(仅适用于某些类型),并且在不使用引用计数的实现中根本不存在这种优化。在库的性能敏感部分中,应改用’’.join()形式。这将确保在各种实现方式中串联发生在线性时间内。

  • 与单例(如None)的比较应该始终使用 is或not进行,永远不要使用相等运算符进行。

    另外,当您真正要表示x不是None时,要当心写x,例如,在测试是否将默认为None的变量或参数设置为其他值时。另一个值可能具有在布尔上下文中可能为false的类型(例如容器)!

  • 使用不是运算符,而不是不是…是。虽然这两个表达式在功能上是相同的,但前者更具可读性,更可取:

# 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> x与x <y交换,y> = x 与x <=
    y交换,并且可以交换x == y和x!= y的参数。的排序()和MIN()操作可保证使用<运算符和MAX()函数使用>
    运算符。但是,最好实施所有六个操作,以免在其他情况下不会造成混淆。

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

# Correct:
def f(x): return 2*x
# Wrong:
f = lambda x: 2*x
  • 第一种形式表示结果函数对象的名称专门为“ f”,而不是通用的“
    < lambda >”。通常,这对于回溯和字符串表示形式更为有用。使用赋值语句消除了lambda表达式可以提供的优于显式def语句的唯一好处(即,它可以嵌入到较大的表达式中)从Exception而不是BaseException派生异常。从BaseException的直接继承保留给捕获异常几乎总是做错事情的异常。

    根据可能需要捕获异常的代码(而不是引发异常的位置)的区别来设计异常层次结构。旨在回答“出了什么问题?”的问题。以编程方式,而不是仅仅声明“发生了问题”(请参阅PEP3151,以了解针对内置异常层次结构学习本课程的示例)

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

  • 适当使用异常链接。在Python 3中,应使用“从Y提高X”来表示显式替换,而不会丢失原始的追溯。

    故意替换内部异常时(在Python 2中使用“提高X”或在Python3.3+中使用“从无提高X”),请确保将相关详细信息转移到新的异常(例如在将KeyError转换为AttributeError时保留属性名称),或将原始异常的文本嵌入新的异常消息中)。

  • 在Python 2中引发异常时,请使用raise ValueError(‘message’)
    而不是较旧的形式引发ValueValue’‘message’。

    后一种形式不是合法的Python 3语法。

    使用括号的形式还意味着,当异常参数很长或包含字符串格式时,由于包含括号,因此不需要使用行继续符。

  • 捕获异常时,请尽可能提及特定的异常,而不要使用裸露的except:子句:

try:
    import platform_specific_module
except ImportError:
    platform_specific_module = None

一个简单的except :子句将赶上SystemExit和一个KeyboardInterrupt异常,使其更难中断与控制-C中的程序,并能掩饰其他问题。如果要捕获所有表示程序错误的异常,请使用 except:除外(单独的except等效于except BaseException:)。

一个好的经验法则是将裸’except’子句的使用限制为两种情况:

1.如果异常处理程序将打印输出或记录回溯;至少用户会意识到发生了错误。
2.如果代码需要做一些清理工作,但是让异常通过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子句限制为所需的绝对最小数量的代码。同样,这避免了掩盖错误:
# 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)
  • 当资源位于特定代码段的本地时,请使用 with语句以确保在使用后立即可靠地对其进行清理。try / finally语句也是可以接受的。
  • 每当他们执行除获取和释放资源以外的其他操作时,都应通过单独的函数或方法来调用上下文管理器:
# 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,并且在函数的末尾(如果可访问)应存在一个显式return语句:
# Correct:

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

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()而不是字符串切片来检查前缀或后缀。

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

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

if isinstance(obj, basestring):

请注意,在Python 3中,unicode和basestring不再存在(仅存在str),bytes对象不再是一种字符串(取而代之的是整数序列)。

  • 对于序列(字符串、列表、元组),请使用空序列为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:
  • 不鼓励在try的finally套件中使用流控制语句return/break/continue…finally,因为流控制语句将跳出finally套件。这是因为这样的语句将隐式取消通过finally套件传播的任何活动异常:
# Wrong:
def foo():
    try:
        1 / 0
    finally:
        return 42

函数注解

随着PEP 484的接受,功能注释的样式规则正在改变。

  • 为了向前兼容,Python 3代码中的函数注释最好应使用PEP 484语法。(在上一节中,有一些关于注释的格式建议。)

  • 不再鼓励使用本PEP中先前建议的注释样式进行实验。

  • 但是,在stdlib之外, 现在鼓励在PEP 484规则之内进行实验。例如,使用PEP 484样式类型注释为大型第三方库或应用程序标记,查看添加这些注释的难易程度,并观察它们的存在是否增加了代码的可理解性。

  • Python标准库在采用此类注释时应保持保守,但允许将其用于新代码和大型重构。

  • 对于想要不同使用功能注释的代码,建议添加以下形式的注释:

# type: ignore

在文件顶部附近;这告诉类型检查器忽略所有注释。(在PEP 484中可以找到更细粒度的方法来阻止类型检查程序的投诉。)

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

  • 不想使用类型检查器的用户可以随意忽略它们。但是,预期第三方库程序包的用户可能希望对那些程序包运行类型检查器。为此, PEP 484建议使用存根文件:类型检查器优先于相应的.py文件读取的.pyi文件。存根文件可以与库一起分发,也可以通过排版的仓库5单独分发(在库作者的允许下)。

  • 对于需要向后兼容的代码,可以以注释的形式添加类型注释。参见PEP 484 6 的相关部分 。


可变注释

PEP 526引入了变量注释。针对它们的样式建议与上述函数注释中的样式建议类似:

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

  • 冒号前不应有空格。

  • 如果一个赋值有一个右手边,那么等号在两边应该恰好有一个空格:

# Correct:

code: int

class Point:
    coords: Tuple[int, int]
    label: str = '<unknown>'
# Wrong:

code:int  # No space after colon
code : int  # Space before colon

class Test:
    result: int=0  # No spaces around equality sign
  • 尽管PEP 526已被Python 3.6接受,但变量注释语法是所有版本的Python上存根文件的首选语法(有关详细信息,请参阅PEP 484)。

版权

该文档已放置在公共领域。

来源:https://github.com/python/peps/blob/master/pep-0008.txt


参考


  1. PEP 7,C代码样式指南,van Rossum ↩︎

  2. Barry’s GNU Mailman style guide http://barry.warsaw.us/software/STYLEGUIDE.txt ↩︎

  3. Donald Knuth’s The TeXBook, pages 195 and 196. ↩︎

  4. http://www.wikipedia.com/wiki/CamelCase ↩︎

  5. Typeshed repo https://github.com/python/typeshed ↩︎

  6. Suggested syntax for Python 2.7 and straddling code https://www.python.org/dev/peps/pep-0484/#suggested-syntax-for-python-2-7-and-straddling-code ↩︎

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值