Python程序员必读—Python PEP8编程风格

目录

基于PEP8的Python编程风格

一、缩进:

二、制表符还是空格

三、最大代码长度:

四、在二元运算符之前或之后是否应该断行

五、空白行:

六、源文件编码:

七、导入:

八、模块级别的dunder名称:

九、行引号:

十、表达式和语句中的空格:

十一、其他建议:

十二、注释:

十三、阻止评论:

十四、内联注释:

十五、文档注释:

十六、命名约定:

十七、凌驾原则:

十八、描述性:命名样式

十九、命名约定:

Pythonic的指导方针:

二十、公共和内部接口:

二十一、编程建议:

二十二、函数注释:


 

基于PEP8的Python编程风格

 

一、缩进:

每个缩进级别使用4个空格
连续行应该使用Python的隐式连线(括号,括号和大括号),或使用悬挂缩进 来垂直对齐包装元素
在使用悬挂式缩进时,应考虑以下内容:第一行应该没有任何争论,应该用进一步的缩进来明确区分自己是一个延续线。
悬挂缩进是一种类型设置样式,其中除了第一行以外,段落中的所有行都是缩进的。
在Python的上下文中,该术语用于描述一种样式,其中,带括号的语句的左括号是该行的最后一个非空白字符,随后的行被缩进,直到右括号。

正确样例:

# 开头分隔符对齐。
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个以内
foo = long_function_name(
  var_one,var_two,
  var_three,var_four)

当if语句的条件部分足够长以至于需要跨越多行书写时,值得注意的是,两个字符关键字(即if)的组合,再加上一个单独的空格,再加上一个左括号,会创建一个自然的四行缩进多行有条件的后续行。
这可能会产生一个嵌套在if语句中的缩进代码套件的视觉冲突,这个套件自然会缩进到4个空格。

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


# 添加评论,这将在编辑器中提供一些区别
# 支持语法高亮。
if (this_is_one_thing and
    that_is_another_thing):
    # 既然这两种情况都是真的,那我们就可以把它说出来。
    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',
)



二、制表符还是空格

空格是首选的缩进方法。
选项卡应该仅用于与已经用选项卡缩进的代码保持一致。
Python 3不允许混合使用制表符和空格来缩进。
Python 2代码缩进与制表符和空格的混合应转换为使用空格专有。

将所有行限制为最多79个字符。
具有较少结构限制(文档或注释)的长文本块,流水线长度应限制为72个字符。
限制所需的编辑器窗口宽度使得可以同时打开多个文件,并且在使用在相邻列中呈现两个版本的代码审阅工具时可以很好地工作。

Python标准库是保守的,需要将行限制为79个字符(文档字符串/注释为72)。
包装长行的首选方法是在括号,括号和大括号内使用Python的隐含行连续。
通过将表达式放在圆括号中,长行可以分成多行。这些应该优先使用反斜杠来续行。

反斜杠有时仍然是合适的。例如,long,多个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())


三、最大代码长度:

将所有行限制为最多79个字符。
具有较少结构限制(文档或注释)的长文本块,流水线长度应限制为72个字符。
限制所需的编辑器窗口宽度使得可以同时打开多个文件,并且在使用在相邻列中呈现两个版本的代码审阅工具时可以很好地工作。

Python标准库是保守的,需要将行限制为79个字符(文档字符串/注释为72)。
包装长行的首选方法是在括号,括号和大括号内使用Python的隐含行连续。
通过将表达式放在圆括号中,长行可以分成多行。这些应该优先使用反斜杠来续行。

反斜杠有时仍然是合适的。例如,long,多个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())

 


四、在二元运算符之前或之后是否应该断行

数十年来,推荐的风格是打破二元运算符。但是这样做可能会以两种方式影响可读性:操作员倾向于在屏幕上的不同列上分散,每个操作员都从操作数移动到前一行。在这里,眼睛必须做额外的工作,以告诉哪些项目被添加,哪些被减去:

# No: 操作员远离他们的操作数

income = (gross_wages +
          taxable_interest +
          (dividends - qualified_dividends) -
          ira_deduction -
          student_loan_interest)

为了解决这个可读性问题,数学家和他们的出版商遵循相反的惯例。
遵循数学的传统通常会产生更具可读性的代码:

# Yes: 容易使操作符与操作数匹配
income = (gross_wages
          + taxable_interest
          + (dividends - qualified_dividends)
          - ira_deduction
          - student_loan_interest)



五、空白行:

用两个空白行围绕顶层函数和类定义。
类中的方法定义被一个空行包围。
可以使用额外的空白行(节省空间)来分隔相关功能组。
在一堆相关的单行程序(例如一组虚拟执行程序)之间可能会省略空白行。
在函数中使用空行来节省逻辑部分。
Python接受控制-L(即^ L)换页字符作为空白; 许多工具将这些字符视为页面分隔符,因此您可以使用它们来分隔文件相关部分的页面。


六、源文件编码:

核心Python发行版中的代码应该始终使用UTF-8
使用ASCII(Python 2)或UTF-8(Python 3)的文件不应该有编码声明。
在标准库中,非默认编码仅用于测试目的,或者当注释或文档字符串需要提及包含非ASCII字符的作者姓名; 否则,使用\ x, \ u,\ U或\ N转义符是将非ASCII数据包含在字符串文本中的首选方法。

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

 


七、导入:

import 时应该按包分行,不要多个包写在同一行。
Yes:

import os
import sys

No: 

import sys, os


可以一个包使用载入多个模块。
from subprocess import Popen, PIPE

import总是放在文件的顶部,在任何模块注释和文档字符串之后,在模块全局变量和常量之前。

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

  • 标准库导入
  • 有关的第三方进口
  • 本地应用程序/库特定的导入


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

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

然而,明确的相对import是绝对import的可接受的替代方案,特别是在处理使用绝对import的复杂包装布局时
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”。

应避免通配符导入(来自<module> import *),因为它们使名称空间中出现的名称不清楚,导致读者和许多自动化工具混淆。

对于通配符导入有一个有效的用例,即重新公布一个内部接口作为公共API的一部分(例如,用可选的加速器模块中的定义覆盖接口的纯Python实现,预先覆盖不了)。
当以这种方式重新发布名称时,下面有关公共和内部接口的准则仍然适用。
 


八、模块级别的dunder名称:

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

"""这是示例模块。


这个模块的东西。
"""


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


复合语句(在同一行上的多个语句)通常是不鼓励的。
是:

if 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
while t < 10: t = delay()

绝对不是:

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

 

十二、注释:

与代码相矛盾的评论比没有评论更糟糕。代码更改时,始终要保持注释的最新状态!
评论应该是完整的句子。如果一个评论是一个短语或句子,它的第一个单词应该大写,除非它是一个以小写字母开头的标识符(不要改变标识符的大小写!)。
如果评论很短,最后的期限可以省略。块注释通常由完整的句子构成的一个或多个段落组成,每个句子应以一段时间结束。
在句末结束后,你应该使用两个空格。
写英文时,请按照Strunk和White。
来自非英语国家的Python程序员:请用英文写下您的意见,除非您确信代码不会被不会说您的语言的人阅读。


 

十三、阻止评论:

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

 

十四、内联注释:

谨慎使用内嵌评论。
内联评论是对语句同一行的评论。内联注释应该与声明至少隔开两个空格。他们应该从# 和一个单一的空间开始。
内联评论是不必要的,事实上,如果他们陈述明显的话,就会分心。
不要这样做:
x = x + 1                 # Increment x
但有时候,这很有用:
x = x + 1                 # Compensate for border

 

十五、文档注释:

编写良好文档字符串(又称“文档”)的惯例在PEP 257中是不朽的。
为所有公共模块,函数,类和方法编写文档。
Docstrings对于非公共方法来说不是必需的,但是您应该有一个评论来描述这个方法的作用。
这个注释应该在def行之后出现。
PEP 257描述了良好的文档字符串约定。请注意,最重要的是,结束多行文档字符串的“”“应该在一行上
例如:

"""返回一个foobang


可选的plotz说首先要对bizbaz进行frobnicate。
"""

对于一个行文档注释,请保持关闭“”“在同一行。

 

十六、命名约定:

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

 

十七、凌驾原则:

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

 

十八、描述性:命名样式

有很多不同的命名风格。它能帮助我们识别使用什么样的命名风格,而不管它们的用途如何。
以下命名风格通常是可区分的:

  • b(单个小写字母)
  • B(单个大写字母)
  • lowercase
  • lower_case_with_underscores
  • UPPERCASE
  • UPPER_CASE_WITH_UNDERSCORES
  • CapitalizedWords(或者CapWords,或者CamelCase,因为它的字母的凹凸不平而得名[4]。这有时也被称为StudlyCaps。
  • 注意:在CapWords中使用缩写时,大写缩写的所有字母。因此HTTPServerError比HttpServerError好。
  • mixedCase(与大写字母不同,由小写字母开始!)
  • Capitalized_Words_With_Underscores(丑!)


还有一种使用简短的唯一前缀来将相关名称组合在一起的风格。这在Python中并没有太多用处,但是为了完整性而提到。
例如:
os.stat()函数返回一个元组,其元素传统上有st_mode,st_size,st_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__。不要发明这样的名字; 只有按照记录使用它们。


 

十九、命名约定:

1、要避免的名称:
切勿将字符'l'(小写字母el),'O'(大写字母oh)或'I'(大写字母)作为单个字符变量名称。
在一些字体中,这些字符与数字1和零是无法区分的。当试图使用“l”时,请使用“L”。

2、封装和模块名称:
模块应该有简短的,全小写的名字。如果提高可读性,则可以在模块名称中使用下划线。尽管不建议使用下划线,Python包也应该有简短的全小写名称。
当用C或C ++编写的扩展模块有一个提供更高级别(例如更多面向对象)接口的伴随Python模块时,C / C ++模块具有前导下划线(例如_socket)。

3、类名称:
类名通常应该使用CapWords约定。
函数的命名约定可以用来代替接口被记录和主要用作可调用的情况。

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

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

5、全局变量名称:
(希望这些变量只能在一个模块中使用)。约定与函数约定相同。
模块是为使用而设计的from M import * 使用的模块应该使用__all__机制来防止导出全局变量,或者使用旧的约定为这样的全局变量添加下划线(您可能想要指出这些全局变量是“模块非公开的“)。

6、函数名称:
函数名称应该是小写的,为了提高可读性,必须使用由下划线分隔的单词。
mixedCase已经是主流风格(例如threading.py)的上下文中保留向后兼容性。

7、函数和方法参数:
始终使用self作为实例方法的第一个参数。
总是使用cls作为类方法的第一个参数。
如果函数参数的名称与保留关键字冲突,通常最好追加一个尾部下划线,而不是使用缩写或拼写损坏。
因此class_比clss好。(也许更好的是通过使用同义词避免这种冲突。)

8、方法名称和实例变量:
使用函数命名规则:小写字母,必要时用下划线分隔,以提高可读性。
只使用一个前导下划线用于非公共方法和实例变量。
为了避免名字与子类冲突,使用两个前导下划线来调用Python的名称修改规则。

Python将这些名称与类名称相冲突:如果类Foo具有名为__a的属性,则Foo.__a将无法访问该属性。
双引号下划线应该只用于避免与设计为子类的类中的属性的名称冲突。

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

10、继承:
总是决定一个类的方法和实例变量(统称为“属性”)是公开的还是非公开的。
如有疑问,请选择非公开; 公开的属性非公开更容易公布。
公共属性是指您希​​望班级中不相关的客户使用的属性,以及避免向后不兼容的更改的承诺。
非公共属性是那些不打算被第三方使用的属性; 你不能保证非公共属性不会改变,甚至不会被删除。
这里我们不使用术语“private”,因为在Python中没有任何属性是真正的私有的(没有通常不必要的工作量)。
另一类属性是“子类API”(通常在其他语言中称为“保护”)的一部分。有些类被设计为继承,扩展或修改类的行为方面。
在设计这样一个类的时候,要注意明确地决定哪些属性是公开的,哪些属性是子类API的一部分,哪些只能被你的基类使用。


 

Pythonic的指导方针

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

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


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

  • 注1:属性仅适用于新式类。
  • 注2:尝试保持功能行为的副作用免费,尽管缓存等副作用一般都很好。
  • 注3:避免使用属性进行计算量大的操作; 该属性符号使得调用者相信访问是(相对)便宜的。


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

  • 注1:请注意,只有简单的类名称用在mangled名称中,所以如果一个子类同时选择了相同的类名和属性名,仍然可以得到名称冲突。
  • 注2:名称修改可以使某些用途,如调试和 __getattr __(),不太方便。然而,名字修改算法是有据可查的,并且易于手动执行。
  • 注3:不是每个人都喜欢名字捣毁。尝试平衡需要避免意外的名称冲突与高级来电者的潜在使用。


 

二十、公共和内部接口:

任何向后兼容性保证只适用于公共接口。因此,用户能够清楚地区分公共和内部接口是重要的。
文档化的接口被认为是公开的,除非文档明确声明它们是临时的或者内部接口免于通常的后向兼容性保证。所有未公开的接口应该被假定为内部的。
为了更好地支持自省,模块应该使用__all__属性在其公共API中显式声明名称。将__all__设置 为空列表表示该模块没有公共API。
即使正确设置了__all__,内部接口(包,模块,类,函数,属性或其他名称)仍应以前导下划线作为前缀。
如果任何包含名称空间(包,模块或类)被认为是内部的,则接口也被认为是内部的。
应始终将导入的名称视为实现细节。其他模块不得间接访问此类导入的名称,除非它们是包含模块的API的明确记录部分,例如os.path或从子模块公开功能的包的__init__模块。

 

二十一、编程建议:

1、代码应该以不影响Python其他实现(PyPy,Jython,IronPython,Cython,Psyco等)的方式编写。
例如,不要依赖于CPython有效地实现就地字符串连接,形式为a + = b 或a = a + b。即使在CPython中,这种优化也很脆弱,在不使用refcounting的实现中完全不存在。
在库的性能敏感部分,应该使用''.join()表单来代替。这将确保串联在各种实现中以线性时间发生。


2、比较像None这样的单件,应该总是用“is”或“is not”,而不是“==”。
另外,如果x的意思是x不是None,那么要小心写x。例如,当测试一个默认为None的变量或参数是否被设置为其他值时。另一个值可能有一个类型(如一个容器),在布尔上下文中可能是false!


3、使用is not 操作而不是 not...is。虽然这两个表达式在功能上是相同的,但前者更具可读性和优选性。

Yes:
if foo is not None:


No:
if not foo is None:

 

4、当具有丰富实施比较排序操作,最好是实现所有六个操作(__eq__,__ne__, __lt__,__le__,__gt__,__ge__)而不是依靠其他代码,只行使特定的比较。
为了尽量减少所涉及的工作,functools.total_ordering() 修饰器提供了一个工具来生成缺少的比较方法。


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

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

Yes:
def f(x): return 2*x

No:
f = lambda x: 2*x

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


6、从Exception派生异常而不是 BaseException。从BaseException直接继承是保留的异常,捕捉它们几乎总是错误的事情。

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

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

7、适当地使用异常链接。在Python 3中,应该使用“从Y提取X”来指示显式替换,而不会丢失原始回溯。
当故意替换内部异常(使用Python 2中的“raise X”或Python 3.3+中的“从None中引发X”)时,确保将相关细节传输到新异常(例如在将KeyError转换为AttributeError时保留属性名称,或将原始异常的文本嵌入到新的异常消息中)。


8、当在Python 2中引发异常时,使用raise ValueError('message')而不是旧的形式引发raise ValueError, 'message'。
后一种形式不是合法的Python 3语法。
paren-using表单也意味着当异常参数很长或包含字符串格式时,由于包含圆括号,所以不需要使用行连续字符。


9、在捕获异常时,尽可能提及特定的异常,而不是使用除了:子句之外的纯粹的异常。
例如,使用:

try:
    import platform_specific_module
except ImportError:
    platform_specific_module = None

一个空的except: 子句将捕获SystemExit和KeyboardInterrupt异常,使得用Control-C中断程序变得困难,并且可以掩盖其他问题。如果要捕获所有发出程序错误的异常,
请使用except Exception:(除了等价于除BaseException外)。

一个很好的经验法则是限制使用裸“除外”从句到两种情况:
如果异常处理程序将打印出或记录回溯; 至少用户会意识到发生了错误。
如果代码需要做一些清理工作,但然后让异常与向上传播 raise. try...finally 可以是一个更好的方式来处理这种情况。

10、当绑定捕获到名称的异常时,更喜欢Python 2.6中添加的显式名称绑定语法:

try:
    process_data()
except Exception as exc:
    raise DataProcessingFailedError(str(exc))

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

11、当捕获操作系统错误时,优先使用Python 3.3中引入的显式异常层次结构,而不是反省errno 值。

12、此外,对于所有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)


13、当资源是特定代码段的本地资源时,请使用 with语句以确保在使用后可以及时可靠地清除资源。try / finally语句也是可以接受的。

14、上下文管理器应该通过独立的函数或者方法来调用,而不是仅仅是获取和释放资源。例如:
Yes:

with conn.begin_transaction():
    do_stuff_in_transaction(conn)

No:

with conn:
    do_stuff_in_transaction(conn)

后面的例子没有提供任何信息来表明__enter__和__exit__方法在事务之后关闭连接以外的其他事情。在这种情况下明确表达是重要的。


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

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)

不好的:

def foo(x):
    if x >= 0:
        return math.sqrt(x)

def bar(x):
    if x < 0:
        return
    return math.sqrt(x)


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

17、使用'.startswith()和''.endswith()而不是字符串切片来检查前缀或后缀。
startswith()和endswith()更清晰,更不容易出错。例如:

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

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


18、对象类型比较应该总是使用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对象不再是一种字符串(而是一个整数序列)

18、对于序列(字符串,列表,元组),使用空序列为假的事实。
好的:

if not seq:
     if seq:

不好的:

if len(seq):
    if not len(seq):


19、不要编写依赖于重要的尾随空格的字符串文字。这样的尾随空白在视觉上难以区分,一些编辑者(或者最近的reindent.py)会修剪它们。

20、不要使用==比较布尔值为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文件。存根文件可以通过库来分发,也可以通过库存作者的许可单独分发[5]。
对于需要向后兼容的代码,可以以注释的形式添加功能注释。参见PEP 484 的相关部分 [6]。

 

翻译自Python官网的PEP8编程规范。

网址:http://legacy.python.org/dev/peps/pep-0008/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值