2019-03-26

2019-03-26  本文已影响0人  瓦解_4417

PEP: 8

标题: Python编码风格指南

作者:
Guido van Rossum guido@python.org,
Barry Warsaw barry@python.org,
Nick Coghlan ncoghlan@gmail.com

状态: 生效

介绍

本文档所提供的编码规范,适用于主要的Python发行版中组成标准库的Python代码。请参阅PEP关于Python的C实现的C编码风格指南的描述 [[[1]].

本文档和PEP257(文档字符串规范)改编自Guido的《Python Style Guide》一文,用Barry的风格指南[[2]]做一些补充。

这篇风格指南随着时间的推移而逐渐演变,随着语言本身的变化,过去的约定已经过时了并确定了更多新的约定。

许多项目都有自己的编码风格指南。如果有任何冲突,优先使用该项目特定的指南。

令人讨厌的小人物身上愚蠢的一致性

Guido的一个重要的见解是,代码阅读的次数比编写的次数多。这里提供的指南旨在提高代码的可读性,并使各种不同的Python代码一致。如PEP20所说,“易读性非常重要”。

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

最重要的是:知道何时会不一致——有时风格指南就不适用了。怀疑时,作出你最佳的判断。看看其他的例子,并决定什么是最好的。不要犹豫,尽管发问!

特别地:不要只为遵从这个PEP而打破向后兼容性!

忽视既定指南的一些其他的好理由:

  1. 当应用指南会降低代码的可读性,即使对于那些习惯遵照这个PEP来阅读代码的人来说。

  2. 与周围的代码保持一致也会破坏它(可能是历史原因)——虽然这也是收拾别人烂摊子的好机会(在真正的XP风格中)。

  3. 因为问题代码先于指南,又没有其它的修改理由。

  4. 代码需要兼容老版本,本风格指南不建议使用的Python特性。

代码布局

缩进

每级缩进使用4个空格。

连续行应该对齐折叠元素,无论是垂直的Python的隐式行连接圆括号内的,中括号内的,大括号内的,还是使用悬挂缩进[[3]]。使用悬挂缩进应注意以下几点;

风格良好:

    # 与分界符对齐
    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)

    # 悬挂缩进应增加一个级别
    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个空格规则是可选的。

可选的:

    # Hanging indents *may* be indented to other than 4 spaces.
    foo = long_function_name(
      var_one, var_two,
      var_three, var_four)

多行if语句:

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',
    )

制表符还是空格?

空格是缩进方法的首选。
制表符仅用于与已经用制表符做缩进的代码保持一致。
Python3不允许混用制表符和空格来缩进。
Python2代码混用制表符和空格缩进,将被转化为只使用空格。
调用Python2命令行解释器时使用-t选项,可对代码中非法混用制表符和空格发出警告。当使用-tt选项,警告将变成错误。这些选项是高度推荐的!

行的最大长度

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

下垂的长块结构限制为更少的文本(文档字符串或注释),行的长度应该限制在72个字符。

限制编辑器窗口宽度使得并排打开多个文件成为可能,并且使用代码审查工具显示相邻列的两个版本工作正常。

绝大多数工具的默认折叠会破坏代码的可视化结构,使其更难以理解。编辑器中的窗口宽度设置为80个字符。即使该工具将在最后一列中标记字形。一些基于网络的工具可能不会提供动态的自动换行。

有些团队强烈喜欢较长的行长度。对于代码维护完全或主要由一个团队的,可以在这个问题上达成协议,象征性的将行长度从80个字符增加到100个字符(有效地增加最大长度到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语句。

确保适当的连续行缩进。

换行应该在二元操作符的前面还是后面?

过去二十年我们都是推荐放在二元操作符的后面。但是这种做法会以两种方式伤害可读性:多个二元操作符在屏幕上不在一列,另外如果你想知道对一个被操作的对象做了什么操作,需要向上找一行。这导致你的眼睛不得不上下往返很多次才能搞清楚哪个数字是被加的,哪个数字是被减的:

    # 风格不良:operators sit far away from their operands
    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(或Python2中用ASCII)。
文件使用ASCII(Python2中)或UTF-8(Python3中)不应有编码声明。

在标准库中,非默认编码仅用于测试目的或注释或文档字符串需要提及包含非ASCII字符的作者名;否则,使用\x\u\U,或\N是字符串中包含非ASCII数据的首先方式。

Python3.0及以上版本,为标准库(参见PEP 3131)规定以下策略:Python标准库中的所有标识符必须使用ASCII标识符,在可行的地方使用英文单词(在很多例子中,使用非英文的缩写和专业术语)。另外,字符串和注释必须用ASCII。仅有的例外是(a)测试非ASCII的特点,(b)测试作者名。不是基于拉丁字母表的作者名必须提供一个他们名字的拉丁字母表的音译。

开源项目面向全球,鼓励采用统一策略。

导入

        import os
        import sys

风格不良:

      风格不良: import sys, os

这样也是可以的:

      from subprocess import Popen, PIPE
      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”来访问。

模块级别的内置属性

模块级别的内置属性(名字有前后双下划线的),例如__all__, __author__, __version__,应该放置在模块的文档字符串后,任意import语句之前,from __future__导入除外。Python强制要求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 os
    import sys

字符串引号

Python中,单引号字符串和双引号字符串是一样的。本PEP不建议如此。建议选择一条规则并坚持下去。当一个字符串包含单引号字符或双引号字符时,使用另一种字符串引号来避免字符串中使用反斜杠。这提高可读性。

三引号字符串,与PEP 257 文档字符串规范一致总是使用双引号字符。

表达式和语句中的空格

无法忍受的

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

      风格良好: spam(ham[1], {eggs: 2})
      风格不良: 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

其它建议

什么时候使用尾部逗号?

尾部逗号通常都是可选的,除了一些强制的场景,比如元组在只有一个元素的时候需要一个尾部逗号。为了代码更加清晰,元组只有一个元素时请务必用括号括起来(语法上没有强制要求):

风格良好:

    FILES = ('setup.cfg',)

技术上可行,但看起来令人困惑:

    FILES = 'setup.cfg',

当尾部逗号不是必须时,如果你用了版本控制系统那么它将很有用。当列表元素、参数、导入项未来可能不断增加时,留一个尾部逗号是一个很好的选择。通常的用法是(比如列表)每个元素独占一行,然后尾部都有逗号,在最后一个元素的下一行写闭标签。如果你的数据结构都是写在同一行的,就没有必要保留尾部逗号了。

风格良好:

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

风格不良:

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

注释

同代码相矛盾的注释比没有注释更差。当代码修改时,始终优先更新注释!

注释应该是完整的句子。如果注释是一个短语或句子,它的第一个单词的首字母应该大写,除非它是一个以小写字母开头的标识符(不更改标识符的情况下!)。

如果注释很短,末尾可以不加句号。注释块通常由一个或多个段落组成,这些段落由完整的句子组成,并且每个句子都应该以句号结尾。

在句尾的句号后边使用两个空格。

写英语注释时,遵循断词和空格。

非英语国家的Python程序员:请用英语书写注释,除非你120%的确定,所有看你代码的人都和你说一样的语言。

注释块

注释块通常适用于一些(或全部)紧跟其后的代码,并且那些代码应使用相同级别的缩进。注释块的每行以一个#和一个空格开始(除非注释里面的文本有缩进)。

注释块内的段落之间由仅包含#的行隔开。

行内注释

谨慎地使用行内注释。

行内注释就是注释和代码在同一行,它与代码之间至少用两个空格隔开。并且它以#和一个空格开始。

如果行内注释指出的是显而易见,那么它就是不必要的。 不要这样做:

    x = x + 1                 # 递增x

但有时,这样是有用的:

    x = x + 1                 # Compensate for border

文档字符串

编写好的文档字符串(即“代码”)约定在PEP 257中是永存的。

      """Return a foobang

      Optional plotz says to frobnicate the bizbaz first.
      """

命名规范

Python库的命名规范有点儿混乱,所以我们不会将他们变得完全一致——不过,这是目前推荐的命名标准。新模块和包(包括第三方框架)应该按这些标准书写,但对有不同的风格的已有库,保持内部一致性是首选。

根本原则

用户可见的API的公开部分的名称,应该遵循反映用法而不是实现的约定。

描述:命名风格

有很多不同的命名风格。它有助于识别使用了什么样的命名风格,这独立于他们的作用。

下面的命名风格是最常见的:

还有一种风格,使用简短独特的前缀组织相关的名字在一起。Python中很少这样用,提一下是为了文档的完整性。例如,os.stat()函数返回一个元祖,它的元素名字通常类似st_modest_sizest_mtime等等这样。(这样做是为了强调与POSIX系统调用结构体一致,这有助于程序员熟悉这些。)

X11库的所有的公开函数以X开头。Python中,这种风格通常认为是不必要的,因为属性名和函数名以对象名作前缀,而函数名以模块名作前缀。

另外,以下特殊形式,前导或后置下划线是公认的(一般可以与任何约定相结合):

      Tkinter.Toplevel(master, class_='ClassName')

规定:命名约定

避免采用的名字

不要使用字符l(小写字母el),O(大写字母oh)或I(大写字母eye)作为单字符变量名。

在某些字体中,这些字符与数字1和0是没有区别的。当想使用l时,用L代替。

ASCII兼容性

标准库中使用的标识符必须与ASCII兼容,详见PEP 3131

包名和模块名

模块名应该短,所有的字母小写。可以在模块名中使用下划线来提高可读性。Python包名也应该短,所有的字母小写,不鼓励使用下划线。

当一个C或C++书写的扩展模块,伴随Python模块来提供了一个更高层次(例如更面向对象)的接口时,C/C++模块名有一个前导下划线(如_socket)。

类名

类名通常使用首字母大写字符串的规则。

函数的命名规则 主要用来可调用的。

在接口被记录并且主要用作调用的情况下,用函数的命名规则来代替类名的命名规则。

注意内置名有一个单独的规则:大多数的内置名是一个单词(或两个单词一起),首字母大写字符串的规则仅用于异常名和内置常量。

类型变量名称

类型变量名称应该首字母大写,并且尽量短,比如:T, AnyStr, Num。对于协变量和有协变行为的变量,建议添加后缀__co或者__contra

    from typing import TypeVar

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

异常名

因为异常应该是类,所以类的命名规则在这里也同样适用。然而,异常名(如果这个异常确实是一个错误)应该使用后缀“Error”。

全局变量名

希望这些变量是在一个模块内使用。)这些规则和那些有关函数的规则是相同的。

模块设计为通过from M import *来使用,应使用all机制防止导出全局变量,或使用加前缀的旧规则,为全局变量加下划线(可能你想表明这些全局变量是“非公开模块”)。

函数和变量名

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

变量名遵循和函数名相同的规定。

混合大小写仅用于这种风格已经占主导地位的上下文(例如threading.py),以保持向后兼容性。

函数和方法参数

使用self做实例化方法的第一个参数。

使用cls做类方法的第一个参数。

如果函数的参数名与保留关键字冲突,最好是为参数名添加一个后置下划线而不是使用缩写或拼写错误。
因此class_ 比clss好。(也许使用同义词来避免更好。)。

方法名和实例变量

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

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

为了避免和子类命名冲突,使用两个前导下划线调用Python的名称改编规则。

Python用类名改编这些名字:如果类Foo有一个属性名为__a,通过Foo.__a不能访问。(可以通过调用Foo._Foo__a来访问。)通常,两个前导下划线仅用来避免与子类的属性名冲突。

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

常量

常量通常定义于模块级别并且所有的字母都是大写,单词用下划线分开。例如MAX_OVERFLOWTOTAL

继承的设计

确定类的方法和实例变量(统称为:“属性”)是否公开。如果有疑问,选择非公开;之后把其变成公开比把一个公开属性改成非公开要容易。

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

非公共属性是那些不打算被第三方使用的,你不能保证非公开的属性不会改变甚至被删除。

此处没有使用术语“private”,因为Python中没有真正私有的属性(没有通常的不必要的工作)。

属性的另一个类别是“API子集”的一部分(在其它语言常被称为“protected”)。某些类被设计为基类,要么扩展,要么修改某些方面的类的行为。在设计这样的类的时候,要注意明确哪些属性是公开的,哪些是API子类的一部分,哪些是真正只在你的基类中使用。

清楚这些之后,这是Python特色的指南:

公共和内部接口

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

文档接口被认为是公开的,除非文档明确声明他们是临时或内部接口,免除通常的向后兼容保证。所有非文档化的接口应假定为内部接口。

为了更好的支持自省,模块应该使用__all__属性显示声明他们公开API的名字, __all__设置为一个空列表表示该模块没有公开API。

即使__all__设置的适当,内部接口(包,模块,类,函数,属性或者其它名字)仍应以一个前导下划线作前缀。

一个接口被认为是内部接口,如果它包含任何命名空间(包,模块,或类)被认为是内部的。

导入名被认为是实现细节。其它模块必须不依赖间接访问这个导入名,除非他们是一个明确的记录包含模块的API的一部分,例如os.path或包的__init__模块,从子模块暴露功能。

程序设计建议

函数注解

随着PEP 484被接受,功能的风格规则注释正在改变。

变量注解

PEP 526引入了变量注释。其风格建议与上述的函数注解类似:

参考文献

[1] PEP 7, Style Guide for C Code, 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

版权

该文件已付诸公示。

上一篇下一篇

猜你喜欢

热点阅读