PEP8翻译

最近闲来无事,重新看了一遍Python PEP8规范,在这里自己翻译一下.

介绍

本文档给出了Python的编码约定,其中主要包含了Python发行中的标准库。请参阅在Python的C实现种为C代码描述的标准配套信息PEP。
本文档和PEP257(Docsting公约)改编自Guido最初的Python风格指南,并增加了一些Barry风格。
代码风格并不是一成不变的,随着时间的推移,过去的惯例也会因为语言本身变化而过时。
许多项目有自己的编码风格指南。发生冲突时,此类项目指南优先于本项目。

愚蠢的使用一致性是无知的妖怪

Guido的主要观点之一是读代码的时候要比写代码的时候多得多。这里提供的准则旨在提高代码的可读性,并使其在各种Python代码中保持一致,如PEP 20所述, “可读性计数”。
风格指南是关于一致性的。一致性无论对于风格指南、一个项目、一个模块还是一个函数来说都是重要的。
然而,也应该知道什么时候应该不一致,当有一些风格不适用的时候。如有疑问,请使用最佳的方案,看其他的例子是如何使用的,不要犹豫去问。
特别的,不要为了适应这个规则而破坏向后兼容性。
一些可以忽略本指南的理由:

  1. 当应用本指南是即使是熟悉本指南的人也会觉得代码丧失易读性。
  2. 为了和其他的代码保持一致(也许是因为出于历史原因)– 尽管这是一个清理别人混乱代码的机会(真正的XP风格)。
  3. 相关代码早于引入准则,那么没有其他理由要修改相关代码。
  4. 代码风格和旧版本Python不兼容,则不需要适应本风格。

代码布局

缩进

使用4空格缩进
连续行应使用Python的隐式行链接括号和大括号,或者使用悬挂缩进垂直赌气包装元素。当时用悬挂缩进是,应考虑以下内容:第一行应该没有任何争论,应使用进一步缩进来表示是续行。

Yes:
# 与开头分隔符对齐
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)

No:
# 没有使用垂直对齐时,禁止把参数放在第一行
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)

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

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

# 没有额外的缩进
if (this_is_one_thing and
    that_is_another_thing):
    do_something()

# 添加注释,这将在编辑器中有所区别
# 支持语法高亮显示
if (this_is_one_thing and
    that_is_another_thing):
    # Since both conditions are true, we can frobnicate.
    do_something()

# 在续行线上添加额外的缩进
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',
)

制表符(Tabs) 或者 空格

空格是首选的缩进方法。
Tabs适用于项目已经使用Tabs缩进的代码保持一致。
Python3不允许混合使用Tabs和空格来缩进。
Python2缩进的代码Tabs和空格的混合应转换为仅使用空格。
当时用-t选项调用Python2解释器时会有非法混合使用Tabs和空格代码的警告。使用-tt时,这些警告会变成错误。强烈建议开启这些选项。

最大行代码数

单行限制79个字符。
对于具有较少结构限制(文档字符串或者注释)的长文本块,每行应限制为72个字符。
限制行代码数量可以调节编辑器窗口宽度,使多个文件可以并排打开,并且在使用相邻列中显示两个版本代码审阅工具时可以很好的工作。
大多数工具包破坏了默认的编码可视化结构,使其更加难以理解。即使是这些工具在最终列提供了标记,这些限制是为了避免在窗口宽度设置为80的编辑器中进行包装。一些基于Web的工具本身不提供动态换行。
有些团队强烈希望更长的行代码数量。对于专门或者可以在此问题上达成共识的团队维护的代码,可以将此标准从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())

另一种情况是assert语句。确保合适的缩进。

应该在二元运算符之前还是之后换行。

几十年来,推荐的方式是在二元运算符之后换行。但是这会出现下面两种情况破坏代码可读性:这样操作数和被操作对象分散在不同行上。在这里,眼睛必须做额外的工作来判断哪些项目是被加上以及哪些被减去。

# No: 运算符远离被操作对象
income = (gross_wages +
          taxable_interest +
          (dividends - qualified_dividends) -
          ira_deduction -
          student_loan_interest)

为了解决这个可读性问题,数学家和他们的出版商遵循相反的惯例。Donald Knuth在他的”计算机与排版”系列中解释了传统的规则:”虽然一段内的公式总是在二进制运算和关系之后中断,但在二进制运算之前,显示的公式总是会中断。”遵循数学的传统通常会有更具可读性的代码:

# Yes: 更容易匹配运算符和操作数
income = (gross_wages
          + taxable_interest
          + (dividends - qualified_dividends)
          - ira_deduction
          - student_loan_interest)

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

空行

在顶层函数和类定义之前空两行。类方法中空一行。可以使用额外的空行来分割相关功能组,在一些相关的单行程序之间可能会省略空行。在函数中使用空行来省略逻辑部分。Python接受C-L作为空格,许多工具将这些字符视为页面分隔符,因此您可以使用他们来分割文件相关部分的页面。请注意,有些编辑器和基于Web的代码查看器可能无法识别他们,并且在其位置显示另一个字符。

源文件编码

核心Python发行版本中的代码应该始终使用UTF-8(Python2使用ASCII)。使用ASCII或者UTF-8的文件不应该有编码声明。在标准库中,非默认编码仅用于测试目的,或者当评论或者文档字符串需要提及包含非ASCII字符的作者姓名,否则,使用\x, \u, \U或者\N 转义字符是将非ASCII数据包含在字符串中首选的方法。对于Python3.0以上的版本,为标准库规定了以下策略:Python标准库中所有标识符必须使用纯ASCII标识符,并且尽可能使用英文单词(许多情况下,缩写和术语并非英语)。另外,字符串和注释也必须使用ASCII。唯一列外的是测试非ASCII功能的测试用例和作者的名称。名称不是基于拉丁字母的作者必须提供这个字符集中他们名字的音译。鼓励全球受众开源项目采取类似政策。

引入

  • 引入通常分开,例如:
Yes: 
import os
import sys

No:  
import sys, os

这也可以:

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

标准库代码应避免复杂的包布局,并始终使用绝对引入。隐式相对引入不应被使用,并且已经在Python3中删除。

  • 从包含类的模块中导入一个类的时候,通常可以如下描述:
from myclass import MyClass
from foo.bar.yourclass import YourClass

如果造成本地名称冲突则:

import myclass
import foo.bar.yourclass

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

  • 应避免使用通配符引入(from <model> import *),因为他们不清楚命名空间中存在哪些名称,使读者和许多自动化工具混淆。对通配符导入有一个有效的用例,及重新发布内部接口作为公共API的一部分(例如,用可选加速器模块的定义覆盖接口的纯Python实现,并确切定义哪些定义预先不知道)。这种方式重新发布时,一下有关公共和内部接口的准则仍然适用。

模块级别的双下(dunder)名称

dunder 的翻译来源于Fluent Python 的译者详见此书第一章注解。(流畅的Python By Luciano Ramalho,安道 吴珂译)

模块级别的”dunders”(即带有两个前导和尾随下划线的名称),例如:__all__, __author__, __vertion__ 等应放在模块文档字符串之后. 但除, __future__ 导入之外的任何导入语句之前, Python要求futured导入必须出现在除文档字符串之外的任何代码之前的模块中。例如:

"""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中的文档字符串一致。

表达式和语句中的空格

宠物的烦恼(无伤大雅的问题)

在下列情况避免使用空格:

  • 在括号或者大括号之内。
Yes: 
spam(ham[1], {eggs: 2})

No:  
spam( ham[ 1 ], { eggs: 2 } )
  • 尾随逗号和后面的右括号之前
  • 在逗号、分号或冒号之前。
  • 然而,在一个切片中,冒号作用类似二元运算符,因此两边应该有相等数量(将其和最低运算符一样优先级对待)。在扩展切片中,两个冒号应该有相同数量间距。例外,当省略切片参数的时候,空格被省略。
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

其他建议

  • 避免在结尾添加空格。因为那些通常是不可见的,因此可能引起混淆,列如,反斜杠后面加一个空格,换行符不会被视为行延续标记,有些编辑器不保留它,许多项目都预先处理拒绝它的钩子。
  • 始终在这些二元运算符两侧使用单空格:赋值 (=), 扩展赋值 (+=, -= 等), 比较 (==, <, >, !=, <>, <=, >=, in, not in, is, is not), 布尔(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)
  • 用于只是关键字参数或者默认参数时,不要在=周围使用空格
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(input: AnyStr): ...
def munge() -> AnyStr: ...
No:

def munge(input:AnyStr): ...
def munge()->PosInt: ...
  • 在参数注释和默认值结合时,请在=周围使用空格(仅适用于那些同时具有注释和默认值的参数)。
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()
  • 虽然有时可以在同一行放置一个ifforwhile等类似的小片段,但是不要为多语句执行此操作,并且避免折叠这么长行。
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()

如何使用结尾逗号

结尾逗号通常是可选的,除了生成一个元组的时候是必须的(Python2中他们具有打印语句的语义)。为了清晰起见,建议用(语法上多余)括号扩住后者。

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编码人员:请用英文写注释,除非您确保您的代码不会被不理解您国家语言的人阅读。

块注释

块注释通常适用于跟随他们的一些(全部)代码,并缩进到同该代码相同的级别。块注释的每一行都以#和单个空格开头(除非他再注释内缩进文本)。块注释中的段落由包含单个#的行分隔

内联注释

请谨慎使用内联注释。内联注释是对语句同一行进行注释,内联注释应该同语句至少保持两个空格,他们应该以#+单空格开始。内联注释是不必要的,事实上如果他们表达明显的话,不要使用他们:

x = x + 1                 # Increment x

但是有时候是有用的:

x = x + 1                 # Compensate for border

文档字符串

编写良好的文档字符串的惯例在PEP 257中不朽。

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

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

对于一行的文档字符串,请"""将放到同一行。

命名约定

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

首要原则

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

描述性: 命名样式

有很多不同的命名风格,它有助于识别使用的命名风格,而不管他们的用途。
以下命名风格通常是可区分的:

  • b(单个小写字母)
  • B (单个大写字母)
  • lowercase
  • lower_case_with_underscores
  • UPPERCASW
  • UPPER_CASE_WITH_UNDERSCORES
  • CapitalizedWords (驼峰命名,注意,驼峰命名在使用字母缩略词时,请大写所有缩略词。因此 HTTPServerError要比HttpServerError要好)
  • mixedCase(与驼峰规则不同首字母小写)
  • Capitalized_Words_With_Underscores(难看!)

还有使用简短的唯一前缀将相关名称组合在一起的风格。这在Python中用处不大,为了完整性提到。例如:os.start()函数返回一个元组,其元素传统上具有诸如st_mode, st_size, st_mtime等名称。(这样做是为了强调与POSIX系统调用结构体的字段的对应关系,有助于程序员熟悉这一点。)
在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__:“魔术”对象或者属性,用户控制命名空间。例如:__init__, __import__ or __file__。除了文档中使用过,请不要使用诸如此类命名。

规定性:命名约定

要避免的名称

不要使用‘l’(小写字母el), ‘O’(大写字母oh), 或者’I’(大写字母eye)或者单字符变量名。在某些字体中,这些字符与数字1和0没有什么区别,当使用‘l’时,请使用‘L’。

ASCII兼容性

标准库中使用的标识符必须是ASCII兼容的,如PEP 3131的表述。

包和模块的名称

模块应该是简短的全小写名称。如果提高可读性,则可以在模块名称中使用下划线,建议尽量不要使用下划线,Python包也应该是简短全小写名称。
当Python模块中存在使用C或者C++编写的更高级别的界都的时候,C或者C++模块加上前导下划线(例如_socket)。

类名称

类名称通常使用CapWords规范,函数名称通常取决于接口文档中的命名情况。注意,內建名称具有单独的约定:大多数內建名称单个单词(或者两个单词),CapWords约定仅用于例外名称和内置常量。

类型变量名

在PEP 484中类型变量名称应该首选简短的CapWords命名:T,AnyStr, Num。推荐奖后缀_co或者_contar添加到相应声明协变或者逆变行为的变量中.例如:

from typing import TypeVar

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

异常名称

由于异常应该是类,因此类名约束适用于此,但是您应该在异常名称上使用后缀“Error”(如果异常本身是错误)。

全局变量名称

(我们希望这些变量只能在一个模块中使用)。约定同函数约定。使用M import *引入的模块应该使用__all__防止导出全局变量,或者按照就得约定为全局变量加上下划线(表明全局变量是“非模块公开的”)。

函数和变量名称

函数名称应该是小写字母,必要时采用下划线分隔单词,以增强其可读性。变量名称应和函数名称遵循相同约定。mixedCase只允许在已经成型的上下文中使用(例如:threadin.py),并保持其向后兼容性。

函数和方法参数

总是使用self作为实例方法的第一个参数。总是使用cls作为类方法的第一个参数。如果函数参数名称同关键字冲突,最好在尾部追加下划线而不是使用缩写或者错误拼写。因此,class_要比clss要好。(也许更好地方式是利用同义词来避免
这种冲突)。

方法名称和实例变量

使用函数命名规范,必要时用利用下划线分隔字母来提高可读性。非公开方法和实例变量加前导下划线。为了避免名称同子类发生冲突,请使用双前导下划线来调用Python的名称矫正规则。
注意:有关使用__names的争议见下文。

常量

常量通常在模块级定义,并使用大写字母和下划线分隔单词。例如:MAX_OVERFLOW和TOTAL。

设计继承

总是提供类方法或者实例变量(通常来说:属性)是公有还是私有,如果不确定,那么选择私有,将公有变量设置为私有通常更加容易。公有属性是指您希望不相关的用户使用的属性,以及您承诺避免向后不兼容的更改的属性。私有属性是那些您不打算为第三方使用的属性,您不保证这些属性不会被更改,甚至是删除。这里我们不使用术语“private”,因为Python没有任何属性是真正私有的(通常是没必要的工作)。另一类属性是属于“子类API”(通常在其他语言中是“受保护”)的属性。有些累被设计为从类继承、扩展或修改类型为。在设计这些类的时候,明确决定哪些属性是公开的,哪些属性是子类API的一部分,哪些只能由基类使用。
考虑到这些下面是Python的指导方针:

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

注1:请参阅上面有关于类方法命名的描述

  • 对于简单的公有数据数学,最后进公开公有属性名称,而不需要复杂的访问器、增量器方法。请记住,如果您发现简单的数据属性需要增加功能行为,那么Python为未来增强提供了一条简单的途径,这种情况下,使用数学因此简单数据数据访问语法功能实现。

注1:属性仅适用于新类(new-style class)
注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)。这些语句在Jython中运行较慢。在性能敏感的库中,应该使用''.join(),这样可以保证在不同的实现种字符拼接时间花费都呈线性。

  • 诸如None这样的字符比较时,请使用is 或者 is not,永远不要使用==判断。

    同样的,测试一个变量或默认参数被设置为其他值的时候(例如:if x 表示if x is not None),要注意,这个值应该有明确表示布尔逻辑的上下文为False类型(比如:容器)。

  • 使用is not 操作符而不是not ... is。尽管这两种表达方式是等价的,但是前者更加易读。

Yes:

if foo is not None:
No:

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 == yx != y的参数.在 sort()min()函数中确保使用<操作符,max()使用 > 操作符。 但是,最好实现全部操作,从而避免在其他地方产生混淆。

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

Yes:

def f(x): return 2*x

No:

f = lambda x: 2*x

第一种形式明确了函数名称是“f”而不是通用的泛型'<lambda>'。一般来说这对追溯和字符串表示更有作用。赋值语句的使用消除了lambda表达式可以提供显示def语句的唯一好处(他可以嵌入更强大表达式中)。

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

可能需要根据代码捕捉异常的区别设计异常层次结构,而不是引发异常的位置。旨在回答“发生了什么问题?”而不是仅仅指出”发生了问题”(参见PEP 3151的內建的异常是一个例子)
类命名约定适用于此,如果异常时错误,则应该加上后缀”Error”,用于非本地流量控制或者其他方式的飞错误异常指令不需要特殊后缀。

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

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

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

    后一种形式不是合法的Python3语法。paren-using表单也意味着异常参数很长或者包含字符串格式,由于有圆括号,因此您不需要使用续行符。

  • 捕捉异常时,尽可能表明特定异常而不是使用except: 的语句。例如:

try:
    import platform_specific_module
except ImportError:
    platform_specific_module = None

一个空的except:语句将会捕获 SystemExitKeyboardInterrrupt 异常。这会使得很难用Control-C来中断一个程序,并且还会隐藏其他的问题。 如果你想捕获一个程序中的所有异常,使用except Exception: (bareexcept BaseException:是等价的)。

经验告诉我们在两个情况下避免使用空except

  • 如果异常处理程序要打印或者记录回溯。至少使用者会意识到发生错误。
  • 如果代码要做一些收尾工作,但是随后要用 raise向上抛出异常。try...finally可以更好地处理这个问题。
  • 当把异常绑定为一个标识符的时候,更喜欢用Python2.6中的方法:
try:
    process_data()
except Exception as exc:
    raise DataProcessingFailedError(str(exc))

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

  • 当捕获操作系统错误时候,首选Python3.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)

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

  • 再返回声明中保持一致。不是所有的函数都应返回一个表达式,否则他们返回None。如果任何return语句返回一个表达式,那么没有返回值的任何返回语句都应该明确声明这是返回None,并且函数末尾应该有一个显式的return语句(如果可以的话)。
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更早的Pythons,则覆盖此规则。
  • 使用''.startswith()''.endswith()代替字符串切片来检查前缀或后缀。
    startswith()endswith()更清晰,不易出错。 例如:
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有一个共同的基类basestring,所以你可以这样做:

if isinstance(obj, basestring):

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

  • 对于序列(字符串、列表、元组),请使用空序列为假判断。
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:

功能注释

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

  • 为了向前兼容,Python 3代码中的函数注释应该最好使用PEP 484语法。(在上一节中,对注释有一些格式化建议。)
  • 不再鼓励在此PEP中以前推荐的注释样式实验。
  • 但是,除了stdlib之外,现在鼓励PEP 484的样式。例如,使用PEP 484风格类型注释标记大型第三方库或应用程序,检查添加这些注释是多么容易,并观察它们的存在是否增加了代码的可读性。
  • Python标准库在采用这些注释时应该保守,但是它们的使用允许用于新代码和大型重构。
  • 对于想要对功能注释进行不同使用的代码,建议对表单单独注释:
 # type: ignore

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

  • 像绒毛一样,类型检查器是可选的独立工具。默认情况下,Python解释器不应该由于类型检查而发出任何消息,并且不应该基于注释来改变它们的行为。
  • 不想使用类型检查器的用户可以自由地忽略它们。但是,第三方库软件包的用户可能希望在这些软件包上运行类型检查程序。为此,PEP 484建议使用存根文件:类型检查器读取的.pyi文件,而不是相应的.py文件。存根文件可以通过库分发,也可以通过the typeshed repo单独分发(通过库作者的许可)。
  • 对于需要向后兼容的代码,可以以注释的形式添加类型注释。参见PEP 484 的相关章节

变量注释

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

参考

PEP 7, Style Guide for C Code, van Rossum

Barry’s GNU Mailman style guide

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

http://www.wikipedia.com/wiki/CamelCase

Typeshed repo 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
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python PEP 8是一种编码风格规范,全称为"Style Guide for Python Code",它主要涵盖了命名规范、注释文档、代码布局和命令规范等方面。 PEP 8提供了一套统一的规则,旨在帮助开发人员编写易读、一致和高效的Python代码。 在编写Python代码时,考虑到其他Python实现的效率问题也是很重要的。例如,在字符串拼接时,使用运算符"+"会在CPython中效率较高,但在Jython中效率非常低。因此,推荐使用.join()方法来进行字符串拼接,以提高代码的性能和可移植性。 遵循PEP 8规范编写代码将使你的代码更易于理解、维护和共享。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Python PEP8 编码规范中文版.zip](https://download.csdn.net/download/hualinux/12692523)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [走进PEP8——代码规范](https://blog.csdn.net/weixin_44352981/article/details/111634884)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Python中PEP8规范说明](https://blog.csdn.net/zsh773992554/article/details/85249331)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值