PEP 8 - Python代码样式指南

时间:2022-02-08 09:38:17

PEP 8 - Python代码样式指南

PEP: 8
标题: Python代码风格指南
作者: Guido van Rossum <python.org上的guido>,Barry Warsaw <python.org上的barry>,Nick Coghlan <ncoghlan at gmail.com>
状态: 活性
类型: 处理
创建: 05 - 07月2001
后的历史: 05-Jul-2001,01-Aug-2013

介绍

本文档给出了Python代码组成的编码约定,其中包含主要Python发行版中的标准库。请参阅在Python的C实现中为C代码描述样式准则的配套信息PEP [1]

本文档和PEP 257(Docstring公约)改编自Guido最初的Python风格指南文章,并增加了一些Barry风格指南[2]

随着时间的推移,这种风格指南会随着其他惯例的确定而变化,过去的惯例会因语言本​​身的变化而过时。

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

愚蠢的一致性是小心灵的大地精

Guido的主要见解之一是代码读取的次数比写入次数多得多。这里提供的准则旨在提高代码的可读性并使其在各种Python代码中保持一致。如PEP 20所述,“可读性计数”。

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

但是,知道什么时候不一致 - 有时风格指导建议不适用。如有疑问,请使用您的最佳判断。看看其他例子,并决定什么看起来最好。不要犹豫,问!

特别是:不要为了符合这个PEP而打破向后兼容性!

其他一些很好的理由可以忽略特定的指导方针:

  1. 在应用指南时,即使对于习惯阅读遵循此PEP的代码的人来说,代码的可读性也会降低。
  2. 为了与周围的代码保持一致(也许是出于历史原因) - 尽管这也是一个清理别人乱七八糟(真正的XP风格)的机会。
  3. 因为有关代码早于引入准则,所以没有其他理由要修改该代码。
  4. 当代码需要与旧版本的Python不兼容时,该版本不支持样式指南推荐的功能。

代码布局

缩进

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

连续行应使用Python的隐式行连接括号,括号和大括号,或使用悬挂缩进 来垂直对齐包装元素[7]。在使用悬挂式缩进时,应考虑以下内容:第一行应该没有任何争论,应该使用进一步的缩进来将自己明确地区分为延续线。

是:

#与开头分隔符对齐。
foo = long_function_name(var_one,var_two,
var_three,var_four) #包含更多缩进区别于其他缩进。
def long_function_name(
var_one,var_two,var_three,
var_four):
打印(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):
打印(var_one)

四维空间规则对于延续线是可选的。

可选的:

#悬挂缩进*可以缩进至4个以内。
foo = long_function_name(
var_one,var_two,
var_three,var_four)

if语句的条件部分足够长以至于需要将其写入多行时,值得注意的是,两个字符关键字(即if)的组合,再加上一个空格以及一个左括号会创建一个自然的多行有条件的后续行使用4空格缩进。这可能会与嵌套在if语句中的缩进代码套件产生视觉冲突,该套件自然会缩进到4个空格。这个PEP没有明确地说明如何(或是否)在if语句中进一步从视觉上区分这些条件行与嵌套套件。这种情况下的可接受选项包括但不限于:

#没有额外的缩进。
如果(this_is_one_thing和
that_is_another_thing):
做一点事() #添加评论,这将在编辑器中提供一些区别
#支持语法高亮显示。
如果(this_is_one_thing和
that_is_another_thing):
#既然这两个条件都是真的,我们可以生气。
做一点事() #在条件延续线上添加一些额外的缩进。
如果(this_is_one_thing
和that_is_another_thing):
做一点事()

(另请参阅下面关于是否在二元运算符之前或之后中断的讨论。)

多行结构中的右括号/括号/括号可以排列在列表的最后一行的第一个非空白字符下,如下所示:

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的工具可能根本不提供动态换行。

有些团队强烈希望更长的线条长度。对于专门或主要由可以就此问题达成一致的团队维护的代码,可以将标称行长度从80个字符增加到100个字符(有效地将最大长度增加到99个字符),条件是仍然包含注释和文档字符串72个字符。

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

包装长行的首选方式是在括号,括号和大括号内使用Python的隐含行连续。通过在圆括号中包装表达式,可以将多条线分成多行。这些应该优先使用反斜杠进行续行。

有时反斜杠可能仍然适用。例如,long,multiple with -statements不能使用隐式延续,所以反斜杠是可以接受的:

打开('/ path / to / some / file / you / want / to / read')作为file_1,\
打开('/ path / to / some / file / being / written','w')作为file_2:
file_2.write(file_1.read())

(参见前面的讨论多if语句对此类多的缺口的进一步思考 -statements。)

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

确保适当缩进续行。

应该在二元运算符之前还是之后断行?

几十年来,推荐的风格是在二元运营商之后打破。但是这会以两种方式伤害可读性:操作员倾向于分散在屏幕上的不同列上,并且每个操作员都从操作数移动到前一行。在这里,眼睛必须做额外的工作来确定哪些项目被添加以及哪些项目被减去:

#否:操作员远离他们的操作数
收入=(gross_wages +
taxable_interest +
(股息 - qualified_dividends) -
ira_deduction -
student_loan_interest)

为了解决这个可读性问题,数学家和他们的出版商遵循相反的惯例。Donald Knuth在他的“ 计算机与排版”系列中解释了传统规则:“尽管二进制操作和关系之后段落中的公式总是中断,但显示的公式总是在二进制操作之前中断” [3]

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

#是:容易使操作符与操作数匹配
收入=(gross_wages
+ taxable_interest
+(股息 - qualified_dividends)
- ira_deduction
- student_loan_interest)

在Python代码中,只要约定在本地一致,就可以在二元运算符之前或之后中断。建议使用新代码Knuth的风格。

空白行

用两个空白行围绕顶层函数和类定义。

一个类中的方法定义被一个空行包围。

可以使用额外的空白行(节省空间)来分隔相关功能组。在一堆相关的单行程序(例如一组虚拟执行程序)之间可能会省略空白行。

在函数中使用空行来节省逻辑部分。

Python接受控件-L(即^ L)换页字符作为空格; 许多工具将这些字符视为页面分隔符,因此您可以使用它们来分隔文件相关部分的页面。请注意,有些编辑器和基于Web的代码查看器可能无法将控件-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)作者的名称。名称不是基于拉丁字母(拉丁语-1,ISO / IEC 8859-1字符集)的作者必须提供这个字符集中他们的名字的音译。

鼓励全球受众开放源码项目采取类似的政策。

进口

  • 进口通常应该分开,例如:

    是的:进口操作系统
    进口系统 No:import sys,os

    可以这样说:

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

    进口应按以下顺序进行分组:

    1. 标准库导入
    2. 相关的第三方进口
    3. 本地应用程序/库特定的导入

    您应该在每组导入之间留出空行。

  • 建议使用绝对导入,因为如果导入系统配置不正确(例如,当某个包中的目录在sys.path中结束时),它们通常更具可读性并且往往表现得更好(或者至少提供更好的错误消息):

    导入mypkg.sibling
    从mypkg导入同级
    来自mypkg.sibling导入示例

    然而,明确的相对进口是绝对进口的可接受的替代方案,特别是在处理复杂的包装布局时,使用绝对进口的情况会不必要地冗长:

    从。导入兄弟姐妹
    来自.sibling导入示例

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

    隐进口相对应永远不会被使用,并在Python 3已被删除。

  • 从包含类的模块中导入一个类时,通常可以这样描述:

    从myclass导入MyClass
    从foo.bar.yourclass导入YourClass

    如果此拼写导致本地名称冲突,则拼写它们

    导入myclass
    导入foo.bar.yourclass

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

  • 应避免使用通配符导入(来自<module> import *),因为它们使名称空间中存在哪些名称不清楚,导致读者和许多自动化工具混淆。对于通配符导入有一个有效的用例,即重新发布内部接口作为公共API的一部分(例如,用可选加速器模块的定义覆盖接口的纯Python实现,并确切定义哪些定义预先覆盖不知道)。

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

模块级别的dunder名称

模块级“dunders”(即名称具有两个前缘和两个纵下划线)如__all____author____version__等应被放置在模块文档字符串之后,但在任何导入语句以外 从__future__进口。Python要求未来 - 导入必须出现在除docstrings以外的任何其他代码之前的模块中。

例如:

“”“这是示例模块。

这个模块做的东西。
“”” 从__future__导入barry_as_FLUFL __all__ = ['a','b','c']
__version__ ='0.1'
__author__ ='Cardinal Biggles' 进口操作系统
进口系统

字符串行情

在Python中,单引号字符串和双引号字符串是相同的。这个PEP不会为此提出建议。选择一个规则并坚持下去。但是,如果字符串包含单引号或双引号字符,请使用另一个避免字符串中的反斜杠。它提高了可读性。

对于三引号字符串,总是使用双引号字符与PEP 257中的docstring约定一致。

表达式和语句中的空格

宠物Peeves

在以下情况下避免无关的空白:

  • 立即在括号,括号或大括号内。

    是:垃圾邮件(火腿[1],{鸡蛋:2})
    否:垃圾邮件(火腿[1],{鸡蛋:2})
  • 尾随逗号和后面的右括号之间。

    是:foo =(0,)
    否:bar =(0,)
  • 在逗号,分号或冒号前面:

    是:如果x == 4:print x,y; x,y = y,x
    否:如果x == 4:print x,y; x,y = y,x
  • 然而,在一个切片中,冒号的作用就像一个二元运算符,并且两边应该有相同的数量(将其视为最低优先级的运算符)。在扩展切片中,两个冒号必须具有相同量的间距。例外:当省略切片参数时,空格被省略。

    是:

    火腿[1:9],火腿[1:9:3],火腿[:9:3],火腿[1:3],火腿[1:9:]
    火腿[下:上],火腿[下:上:],火腿[下::步]
    火腿[低+偏移:高+偏移]
    火腿[:upper_fn(x):step_fn(x)],火腿[:: step_fn(x)]
    火腿[低+偏移:高+偏移]

    没有:

    火腿[低+偏移:高+偏移]
    火腿[1:9],火腿[1:9],火腿[1:9:3]
    火腿[下::上]
    火腿[:上]
  • 紧接在开始函数调用参数列表的开括号之前:

    是:垃圾邮件(1)
    否:垃圾邮件(1)
  • 紧接在开始索引或切片的开括号之前:

    是:dct ['key'] = lst [index]
    否:dct ['key'] = lst [index]
  • 在一个赋值(或其他)运算符周围的多个空间将其与另一个对齐。

    是:

    x = 1
    y = 2
    long_variable = 3

    没有:

    x = 1
    y = 2
    long_variable = 3

其他建议

  • 避免在任何地方拖曳空白。因为它通常是不可见的,所以可能会引起混淆:例如,反斜杠后跟一个空格,换行符不会被视为行延续标记。有些编辑不保留它,许多项目(如CPython本身)都预先提交了拒绝它的钩子。

  • 始终围绕这些二元运算符在任一侧使用一个空格:赋值(=),扩充赋值(+ =- = 等),比较(==<>!=<><=, > =不在是不是),布尔值(, )。

  • 如果使用具有不同优先级的运营商,请考虑在优先级最低的运营商周围添加空白。使用你自己的判断; 但是,从不使用多个空格,并且在二元运算符的两侧始终具有相同数量的空白。

    是:

    我=我+ 1
    提交+ = 1
    x = x * 2 - 1
    hypot2 = x * x + y * y
    c =(a + b)*(ab)

    没有:

    I = I + 1
    提交+ = 1
    x = x * 2 - 1
    hypot2 = x * x + y * y
    c =(a + b)*(a-b)
  • 用于指示关键字参数或默认参数值时,不要在=符号周围使用空格。

    是:

    def complex(real,imag = 0.0):
    回归魔术(r =真实,i =形象)

    没有:

    def complex(real,imag = 0.0):
    回归魔术(r =真实,i =形象)
  • 功能注释应该使用冒号的正常规则,并且如果存在的话,总是在- >箭头周围留有空格。(有关功能注释的更多信息,请参见 下面的函数注释。)

    是:

    def munge(输入:AnyStr):...
    def munge() - > AnyStr:...

    没有:

    def munge(输入:AnyStr):...
    def munge() - > PosInt:...
  • 将参数注释与默认值组合时,请在=符号周围使用空格(但仅适用于那些同时具有注释和默认值的参数)。

    是:

    def munge(sep:AnyStr = None):...
    def munge(输入:AnyStr,sep:AnyStr = None,限制= 1000):...

    没有:

    def munge(输入:AnyStr =无):...
    def munge(输入:AnyStr,限制= 1000):...
  • 复合语句(在同一行上的多个语句)通常是不鼓励的。

    是:

    如果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
    而t <10时:t =延迟()

    当然不:

    if foo =='blah':do_blah_thing()
    else:do_non_blah_thing() 试试:something()
    最后:清理() do_one(); do_two(); do_three(long,argument,
    列表,就像这样) 如果foo =='blah':one(); 二(); 三()

何时使用尾随逗号

尾随逗号通常是可选的,除了在制作一个元素的元组时是必须的(在Python 2中它们具有打印语句的语义)。为了清楚起见,建议用(技术上冗余的)括号括住后者。

是:

FILES =('setup.cfg',)

好,但令人困惑:

FILES ='setup.cfg',

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

是:

FILES = [
'setup.cfg',
'tox.ini',
]
初始化(文件,
错误=真,

没有:

FILES = ['setup.cfg','tox.ini',]
初始化(FILES,错误= True,)

注释

与代码相抵触的评论比没有评论更糟糕。在代码更改时,始终优先保持评论的最新状态!

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

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

除了最后一句之外,您应该在多句话评论的句末结束后使用两个空格。

在写英文时,请遵循Strunk和White。

来自非英语国家的Python编码人员:请用英文写下您的意见,除非您确信代码不会被不会说您的语言的人阅读。

阻止评论

块注释通常适用于跟随它们的一些(或全部)代码,并缩进到与该代码相同的级别。块注释的每一行都以和单个空格开头(除非它在注释内缩进文本)。

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

内联注释

谨慎使用内嵌评论。

内联评论是对语句同一行的评论。内联注释应该与语句中的至少两个空格分隔。他们应该以#和单个空间开始。

内联评论是不必要的,事实上,如果他们陈述明显的话,就会分心。不要这样做:

x = x + 1#增量x

但有时候,这很有用:

x = x + 1#补偿边界

文档字符串

编写良好文档字符串(又称“文档字符串”)的惯例在PEP 257中不朽。

  • 为所有公共模块,函数,类和方法编写文档。Docstrings对于非公开方法不是必需的,但您应该有一个评论来描述该方法的功能。这条评论应该出现在def行之后。

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

    “”“回到foobang
    
    可选的plotz说先激烈地推销bizbaz。
    “””
  • 对于一个班轮文件,请将结尾“”“保留在同一行。

命名约定

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

首要原则

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

描述性:命名样式

有很多不同的命名风格。它有助于识别使用的命名风格,而不管它们的用途。

以下命名风格通常是可区分的:

  • b(单个小写字母)

  • B(单个大写字母)

  • 小写

  • lower_case_with_underscores

  • 大写

  • UPPER_CASE_WITH_UNDERSCORES

  • CapitalizedWords(或CapWords,或者驼峰-如此,因为它信件的颠簸外观而得名[4])。这有时也被称为StudlyCaps。

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

  • mixedCase(与大写字母不同,由小写字母开始!)

  • Capitalized_Words_With_Underscores(丑陋!)

还有使用简短的唯一前缀将相关名称组合在一起的风格。这在Python中用处不大,但为了完整性而提到。例如,os.stat()函数返回一个元组,其元素传统上具有诸如st_mode, st_sizest_mtime等名称。(这样做是为了强调与POSIX系统调用结构体的字段的对应关系,这有助于程序员熟悉这一点。)

X11库为其所有公共功能使用了领先的X. 在Python中,这种风格通常被认为是不必要的,因为属性和方法名称以一个对象为前缀,而函数名称以模块名称为前缀。

此外,使用前导或尾随下划线的以下特殊表格被认可(这些通常可以与任何案例惯例结合使用):

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

  • 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'(大写字母)作为单个字符变量名称。

在某些字体中,这些字符与数字1和0无法区分。当试图使用'l'时,请使用'L'。

ASCII兼容性

如描述的标准库使用的标识符必须是ASCII兼容 政策课 的PEP 3131

包和模块名称

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

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

类名称

类名通常应该使用CapWords约定。

函数的命名约定可用于接口被记录并主要用作可调用函数的情况。

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

输入变量名称

PEP 484中引入的类型变量的名称通常应该使用首选短名称的CapWordsTAnyStrNum。建议将后缀_co_contra添加到用于相应地声明协变或逆变行为的变量中。例子:

从键入import TypeVar

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

例外名称

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

全局变量名称

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

设计用于通过M import *使用的模块应该使用__all__机制来防止导出全局变量,或者使用旧的约定为这些全局变量加下划线(为了表明这些全局变量是“模块非公开的“)。

函数和变量名称

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

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

mixedCase只允许在已经是流行风格的上下文中使用(例如threading.py),以保持向后兼容性。

函数和方法参数

总是使用self作为实例方法的第一个参数。

总是使用cls作为类方法的第一个参数。

如果函数参数的名称与保留关键字冲突,通常最好追加一个尾部下划线而不是使用缩写或拼写损坏。因此,class_clss好。(也许更好的是通过使用同义词来避免这种冲突。)

方法名称和实例变量

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

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

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

Python将这些名称与类名称相冲突:如果类Foo具有名为__a的属性,则Foo .__ a将无法访问该属性。(一个持久的用户仍然可以通过调用Foo._Foo__a来获得访问。)通常,双引号下划线应该仅用于避免名称与设计为子类的类中的属性冲突。

注意:有关使用__names的争议(见下文)。

常量

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

设计继承

总是要决定一个类的方法和实例变量(统称为“属性”)是公开的还是非公开的。如有疑问,请选择非公开; 将公开属性设置为非公开更容易。

公共属性是指您希​​望班级中不相关的客户使用的属性,以及您承诺避免向后不兼容的更改的属性。非公开属性是那些不打算供第三方使用的属性; 您不保证非公开属性不会更改,甚至不会被删除。

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

另一类属性是属于“子类API”(通常在其他语言中称为“受保护”)的属性。有些类被设计为从类继承,扩展或修改类的行为方面。在设计这样的类时,注意明确地决定哪些属性是公开的,哪些属性是子类API的一部分,哪些只能由基类使用。

考虑到这一点,这里是Pythonic的指导方针:

  • 公共属性应该没有前导下划线。

  • 如果您的公共属性名称与保留关键字冲突,请在属性名称后附加一个尾部下划线。这比缩写或拼写错误更可取。(但是,尽管有这条规则,'cls'是任何已知为类的变量或参数的首选拼写,尤其是类方法的第一个参数。)

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

  • 对于简单的公共数据属性,最好仅公开属性名称,而不需要复杂的访问器/增量器方法。请记住,如果您发现简单的数据属性需要增加功能行为,则Python为将来的增强提供了一条简单的途径。在这种情况下,使用属性隐藏简单数据属性访问语法后面的功能实现。

    注1:属性仅适用于新式类。

    注2:尝试保持功能行为的副作用免费,尽管缓存等副作用通常很好。

    注3:避免使用属性进行计算量大的操作; 该属性符号使得调用者相信访问是(相对)便宜的。

  • 如果你的类想要被子类化,并且你有不希望子类使用的属性,考虑用双引号下划线命名它们,并且不要使用尾随下划线。这将调用Python的名称修改算法,其中该类的名称被修改为属性名称。这有助于避免属性名称冲突,如果子类无意中包含具有相同名称的属性。

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

    注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()表单来代替。这将确保串联在各种实现中以线性时间发生。

  • 像None这样的单身人士的比较应该总是用“ 是”或“ 不是”来进行,从来就不是平等运算符。

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

  • 使用不是操作员而不是...是。虽然这两个表达式在功能上是相同的,但前者更具可读性并且更受欢迎。

    是:

    如果foo不是无:
    

    没有:

    如果不是foo是None:
    
  • 当具有丰富实施比较排序操作,最好是实现所有六个操作(__eq____ne__, __lt____le____gt____ge__)而不是依靠其他代码,只行使特定的比较。

    为了尽量减少所涉及的工作,functools.total_ordering() 修饰器提供了一个工具来生成缺少的比较方法。

    PEP 207指出Python 反射规则由Python承担的。因此,解释器可以用x <yy> = x 与x <= y交换y> x,并且可以交换x == yx!= y的参数。的排序()MIN()操作可保证使用<运算符和MAX()函数使用> 运算符。但是,最好是执行所有六项操作,以免在其他情况下出现混淆。

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

    是:

    def f(x):return 2 * x
    

    没有:

    f = lambda x:2 * x
    

    第一种形式意味着结果函数对象的名称特别是'f'而不是泛型'<lambda>'。一般而言,这对追溯和字符串表示更有用。赋值语句的使用消除了lambda表达式可以提供的明确def语句的唯一好处(即它可以嵌入到更大的表达式中)

  • Exception中派生异常而不是BaseException。从BaseException直接继承保留用于捕获它们的异常几乎总是错误的事情。

    可能需要根据代码捕捉异常的区别设计异常层次结构 ,而不是引发异常的位置。旨在回答“发生了什么问题?”的问题 以编程方式,而不是仅仅指出“发生了问题”(参见PEP 3151为内建的异常层次学习本课的一个例子)

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

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

    当故意替换内部异常(使用Python 2中的“raise X”或Python 3.3+中的“从None中引发X”)时,确保将相关细节传输到新异常(例如在将KeyError转换为AttributeError时保留属性名称,或将原始异常的文本嵌入到新的异常消息中)。

  • 当在Python 2中引发异常时,使用raise ValueError('message') 而不是旧的形式引发ValueError,'message'

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

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

  • 捕捉异常时,尽可能提及特定的异常,而不是使用除了::的子句之外的其他异常。

    例如,使用:

    尝试:
    导入platform_specific_module
    除了ImportError:
    platform_specific_module =无

    除外:子句将赶上SystemExit和一个KeyboardInterrupt异常,使其更难中断与控制-C中的程序,并能掩饰其他问题。如果要捕获所有发出程序错误的异常,请使用 except Exception(除了等价于除BaseException之外的bare:。

    一个好的经验法则是在两种情况下限制使用裸“除”条款:

    1. 如果异常处理程序将打印输出或记录回溯; 至少用户会意识到发生了错误。
    2. 如果代码需要做一些清理工作,但随后让异常向上传播并提升。 尝试......终于 可以成为处理这种情况的更好方法。
  • 当绑定捕获到名称的异常时,更喜欢Python 2.6中添加的显式名称绑定语法:

    尝试:
    处理数据()
    除了作为exc的例外:
    引发DataProcessingFailedError(str(exc))

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

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

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

    是:

    尝试:
    值=集合[键]
    除KeyError外:
    返回key_not_found(键)
    其他:
    return handle_value(value)

    没有:

    尝试:
    # 太宽泛!
    return handle_value(collection [key])
    除KeyError外:
    #也会捕获handle_value()引发的KeyError
    返回key_not_found(键)
  • 当某个资源是本地代码的特定部分时,请使用 with语句以确保在使用后可以及时可靠地清理它。try / finally语句也是可以接受的。

  • 上下文管理器应该通过独立的函数或方法来调用,只要它们不是获取和释放资源而是执行其他操作。例如:

    是:

    与conn.begin_transaction():
    do_stuff_in_transaction(康涅狄格州)

    没有:

    与conn:
    do_stuff_in_transaction(康涅狄格州)

    后一个例子没有提供任何信息来表明__enter____exit__方法除了在事务之后关闭连接之外正在做其他事情。在这种情况下,明确是很重要的。

  • 在回报声明中保持一致。函数中的所有返回语句都应该返回一个表达式,或者它们都不应该。如果任何return语句返回一个表达式,那么没有返回值的任何返回语句都应该明确声明这是返回None,并且函数末尾应该有一个显式的return语句(如果可以的话)。

    是:

    def foo(x):
    如果x> = 0:
    返回math.sqrt(x)
    其他:
    返回无 def bar(x):
    如果x <0:
    返回无
    返回math.sqrt(x)

    没有:

    def foo(x):
    如果x> = 0:
    返回math.sqrt(x) def bar(x):
    如果x <0:
    返回
    返回math.sqrt(x)
  • 使用字符串方法而不是字符串模块。

    字符串方法总是快得多,并与unicode字符串共享相同的API。如果需要向后兼容比2.0更早的Pythons,则覆盖此规则。

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

    startswith()和endswith()更清晰,不易出错。例如:

    是的:如果foo.startswith('bar'):
    No:if foo [:3] =='bar':
  • 对象类型比较应始终使用isinstance(),而不是直接比较类型。

    是:如果isinstance(obj,int):
    
    No:如果type(obj)是type(1):
    

    当检查一个对象是否是一个字符串时,请记住它也可能是一个unicode字符串!在Python 2中,str和unicode有一个共同的基类basestring,所以你可以这样做:

    如果isinstance(obj,basestring):
    

    请注意,在Python 3中,unicodebasestring不再存在(只有str),并且一个bytes对象不再是一种字符串(它是一个整数序列)

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

    是的:如果不是seq:
    如果seq: 否:如果len(seq):
    如果不是len(seq):
  • 不要编写依赖于重要尾随空白的字符串文字。这样的尾随空白在视觉上难以区分,一些编辑(或最近的reindent.py)会修剪它们。

  • 不要使用==将布尔值与True或False进行比较。

    是的:如果问候语:
    否:如果问候语== True:
    更糟的是:如果问候语是真的:

功能注释

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

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

  • 不再鼓励在此PEP中以前推荐的注释样式实验。

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

  • Python标准库在采用这些注释时应该保守,但是它们的使用允许用于新代码和大型重构。

  • 对于想要对功能注释进行不同使用的代码,建议对表单发表评论:

    #类型:忽略
    

    靠近文件的顶部; 这告诉类型检查器忽略所有注释。(在PEP 484中可以找到更多细致的关于禁止类型检查者投诉的方法。)

  • 像绒毛一样,类型检查器是可选的独立工具。默认情况下,Python解释器不应该由于类型检查而发出任何消息,并且不应该基于注释来改变它们的行为。

  • 不想使用类型检查器的用户可以*地忽略它们。但是,预计第三方库软件包的用户可能希望在这些软件包上运行类型检查程序。为此, PEP 484建议使用存根文件:.pyi文件,由类型检查器读取而不是相应的.py文件。存根文件可以通过库分发,也可以通过类型化的回购单独分发(通过库作者的许可)[5]

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

变量注释

PEP 526引入了变量注释。他们的风格建议与上面介绍的功能注释类似:

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

  • 冒号前应该没有空格。

  • 如果任务有右手边,那么等号在两边都应该有一个空格。

  • 是:

    代码:int
    
    班级点:
    coords:Tuple [int,int]
    label:str ='<unknown>'
  • 没有:

    code:int#冒号后没有空格
    code:int#冒号前的空格 班级考试:
    结果:int = 0#等号周围没有空格
  • 虽然PEP 526被Python 3.6所接受,但变量注释语法是所有Python版本中存根文件的首选语法(详见PEP 484)。

脚注

[7] 挂入缩进是一种类型设置样式,其中段落中的所有行都缩进,除了第一行。在Python的上下文中,该术语用于描述一种样式,其中,带括号的语句的左括号是该行的最后一个非空格字符,随后的行将缩进到右括号。

参考

[1] PEP 7,C代码风格指南,van Rossum
[2] Barry的GNU Mailman风格指南 http://barry.warsaw.us/software/STYLEGUIDE.txt
[3] Donald Knuth的The TeXBook,第195和196页。
[4] http://www.wikipedia.com/wiki/CamelCase
[5] 类型回购 https://github.com/python/typeshed
[6] 为Python 2.7建议的语法和跨界代码 https://www.python.org/dev/peps/pep-0484/#suggested-syntax-for-python-2-7-and-straddling-code

版权

该文件已被置于公共领域。

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