Python PEP8代码规范

文章内容

Python PEP8原则

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

这是一份关于一致性的风格指南。这份风格指南的风格一致性是非常重要的。更重要的是项目的风格一致性。在一个模块或函数的风格一致性是最重要的。

一、代码布局Code lay-out

1、缩进Indentation

4个空格的缩进(编辑器都可以完成此功能),不要使用Tab,更不能混合使用Tab和空格。

续行应该与其包裹元素对齐,要么使用圆括号、方括号和花括号内的隐式行连接来垂直对齐,要么使用挂行缩进对齐。当使用挂行缩进时,应该考虑到第一行不应该有参数,以及使用缩进以区分自己是续行。

推荐:

# 与左括号对齐
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)

2、悬挂缩进

悬挂缩进不一定是4个空格

四空格的规则对于续行是可选的:

# 挂行缩进不一定要用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',
)

3、行的最大长度Maximum Line Length

每行最大长度79,换行可以使用反斜杠,最好使用圆括号。换行点要在操作符的后边敲回车。 文本长块,比如文档字符串或注释,行长度应限制为72个字符。

4、空行Blank Lines

  • 类和top-level函数定义之间空两行
  • 类中的方法定义之间空一行
  • 函数内逻辑无关段落之间空一行
  • 其他地方尽量不要再空行

5、复合语句(同一行中的多个语句)通常是不允许的。

推荐:

if foo == 'blah':
    do_blah_thing()
do_one()
do_two()
do_three()

不推荐:

if foo == 'blah': do_blah_thing()
do_one(); do_two(); do_three()

6、虽然有时候将小的代码块和 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()

二、源文件编码Source File Encoding

  • 核心Python发行版中的代码应始终使用UTF-8(或Python 2中的ASCII)。
  • 使用ASCII(在Python 2中)或UTF-8(在Python 3中)的文件不应该有编码声明。
  • Python标准库中的所有标识符必须使用纯ASCII标识符,并且应尽可能使用英文单词(在许多情况下,缩写和技术使用的术语不是英语)。
  • 字符串文字和注释也必须使用ASCII。

三、导入Imports

  • 导入始终在文件的顶部,在模块注释和文档字符串之后,在模块全局变量和常量之前。
  • 不同模块分行导入,内置模块放在前面,第三方模块在后面导入
  • 避免通配符的导入(from import *),因为这样做会不知道命名空间中存在哪些名字,会使得读取接口和许多自动化工具之间产生混淆。

推荐:

import os
import sys
from subprocess import Popen, PIPE

不推荐:

from xxx import *

导入应该按照以下顺序分组:

  1. 标准库导入
  2. 相关第三方库导入
  3. 本地应用/库特定导入应该在每一组导入之间加入空行。

四、表达式和语句中的空格

1、在以下情况下避免无关的空白:括号或大括号内

Yes: spam(ham[1], {eggs: 2})
No:  spam( ham[ 1 ], { eggs: 2 } )

2、尾随逗号和后面的右括号之间

Yes: foo =(0,)
No: bar=(0,)

3、在逗号,分号或冒号前面

Yes: if x == 4: print x, y; x, y = y, x
No:  if x == 4 : print x , y ; x , y = y , x

4、在一个切片中,冒号的作用就像一个二元运算符,并且两边应该有相同的数量;在扩展切片中,两个冒号必须具有相同量的间距。例外:当省略切片参数时,空格被省略

推荐:

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]

不推荐:

ham[lower + offset:upper + offset]
ham[1: 9], ham[1 :9], ham[1:9 :3]
ham[lower : : upper]
ham[ : upper]

5、函数的调用,小括号之前

Yes: spam(1)
No:  spam (1)

6、在一个赋值(或其他)运算符周围的多个空间将其与另一个对齐

推荐:

x = 1
y = 2
long_variable = 3

不推荐:

x             = 1
y             = 2
long_variable = 3

7、二元运算符两边放置一个空格,涉及 = 符合操作符 ( += , -=等)、比较( == , < , > , != , <> , <= , >= , in , not in , is , is not )、布尔( and , or , not )。优先级高的运算符或操作符的前后不建议有空格

推荐:

i = i + 1
submitted += 1
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)

不推荐:

i=i+1
submitted +=1
x = x * 2 - 1
hypot2 = x * x + y * y
c = (a + b) * (a - b)

8、关键字参数和默认值参数的前后不要加空格

推荐:

def complex(real, imag=0.0):
    return magic(r=real, i=imag)

不推荐:

def complex(real, imag = 0.0):
    return magic(r = real, i = imag)

9、避免在尾部添加空格。

因为尾部的空格通常都看不见,会产生混乱。比如:一个反斜杠后面跟一个空格的换行符,不算续行标记。有些编辑器不会保留尾空格,并且很多项目(像CPython)在pre-commit的挂钩调用中会过滤掉尾空格。

10、功能型注释应该使用冒号的一般性规则,并且在使用->的时候要在两边加空格。

推荐:

def munge(input: AnyStr): ...
def munge() -> AnyStr: ...

不推荐:

def munge(input:AnyStr): ...
def munge()->PosInt: ...

11、当给有类型备注的参数赋值的时候,在=两边添加空格(仅针对那种有类型备注和默认值的参数)。

推荐:

def munge(sep: AnyStr = None): ...
def munge(input: AnyStr, sep: AnyStr = None, limit=1000): ...

不推荐:

def munge(input: AnyStr=None): ...
def munge(input: AnyStr, limit = 1000): ...

五、注释Comments

  • 与代码相矛盾的注释比没有注释还糟,当代码更改时,优先更新对应的注释!
  • 注释应该是完整的句子。如果一个注释是一个短语或句子,它的第一个单词应该大写,除非它是以小写字母开头的标识符(永远不要改变标识符的大小写!)。
  • 如果注释很短,结尾的句号可以省略。块注释一般由完整句子的一个或多个段落组成,并且每句话结束有个句号。
  • 在句尾结束的时候应该使用两个空格。
  • 当用英文书写时,遵循Strunk and White (译注:《Strunk and White, The Elements of Style》)的书写风格。
  • 在非英语国家的Python程序员,请使用英文写注释,除非你120%的确信你的代码不会被使用其他语言的人阅读。

1、块注释Block Comments

  • 块注释通常适用于跟随它们的某些(或全部)代码,并缩进到与代码相同的级别。块注释的每一行开头使用一个#和一个空格(除非块注释内部缩进文本)。
  • 块注释内部的段落通过只有一个#的空行分隔。

2、行内注释Inline Comments

  • 内联注释是对语句同一行的注释。内联注释应该与语句中的至少两个空格分隔。
  • 内联注释如果不是必要添加,一般不要加注释,这样容易造成编码关注重心分离。

有节制地使用行内注释。


行内注释是与代码语句同行的注释。行内注释和代码至少要有两个空格分隔。注释由#和一个空格开始。

事实上,如果状态明显的话,行内注释是不必要的,反而会分散注意力。比如说下面这样就不需要:

x = x + 1                 # Increment x

但有时,这样做很有用:

x = x + 1                 # Compensate for border

六、文档字符串

  • 要为所有的公共模块,函数,类以及方法编写文档说明。非公共的方法没有必要,但是应该有一个描述方法具体作用的注释。这个注释应该在def那一行之后。
  • PEP 257 描述了写出好的文档说明相关的约定。特别需要注意的是,多行文档说明使用的结尾三引号应该自成一行。
  • 对于单行的文档说明,尾部的三引号应该和文档在同一行。

七、命名规范Naming Conventions

1、应避免的名字(Names to Avoid)

尽量不要用字符’l'(小写字母el),’O'(大写字母oh),或 ‘I'(大写字母eye) 作为单个字符的变量名。一些字体中,这些字符不能与数字1和0区别。用’L’ 代替’l’时。

2、包和模块名(Package and Module Names)

模块名要简短,全部用小写字母,可使用下划线以提高可读性。包名和模块名类似,但不推荐使用下划线。

3、类名(Class Names)

类名通常使用,大驼峰命名方式

4、函数名(Function Names)

函数名应该小写,如果想提高可读性可以用下划线分隔。大小写混合仅在为了兼容原来主要以大小写混合风格的情况下使用(比如 threading.py),保持向后兼容性。

5、函数和方法参数(Function and method arguments)

  • 总是使用self作为实例方法的第一个参数;
  • 总是使用cls作为类方法的第一个参数。
  • 如果函数的参数名和已有的关键词冲突,在最后加单一下划线比缩写或随意拼写更好。因此 class_ 比 clss 更好。

6、方法名称和实例变量(Method Names and Instance Variables)

  • 使用函数命名规则:必要时用小写字母分隔下划线,以提高可读性
  • 在非共有方法和实例变量前使用单下划线。
  • 通过双下划线前缀触发Python的命名转换规则来避免和子类的命名冲突。

Python通过类名对这些命名进行转换:如果类 Foo 有一个叫 __a 的成员变量, 它无法通过 Foo.__a 访问。(执着的用户可以通过 Foo._Foo__a 访问。)一般来说,前缀双下划线用来避免类中的属性命名与子类冲突的情况。

7、异常名(Exception Names)

因为异常一般都是类,所有类的命名方法在这里也适用。然而,需要在异常名后面加上“Error”后缀(如果异常确实是一个错误)。

8、全局变量名(Global Variable Names)

通过 from M import * 导入的模块应该使用all机制去防止内部的接口对外暴露,或者使用在全局变量前加下划线的方式(表明这些全局变量是模块内非公有)。

9、常量(Constants)

常量通常定义在模块级,通过下划线分隔的全大写字母命名。例如: MAX_OVERFLOW 和 TOTAL。

10、继承的设计(Designing for inheritance)

  • 公共属性不应该有前缀下划线。
  • 如果公共属性名和关键字冲突,在属性名之后增加一个下划线。这比缩写和随意拼写好很多。(然而,尽管有这样的规则,在作为参数或者变量时,‘cls’是表示‘类’最好的选择,特别是作为类方法的第一个参数。)
  • 对于单一的共有属性数据,最好直接对外暴露它的变量名,而不是通过负责的 存取器(accessor)/突变(mutator) 方法。请记住,如果你发现一个简单的属性需要成长为一个功能行为,那么Python为这种将来会出现的扩展提供了一个简单的途径。在这种情况下,使用属性去隐藏属性数据访问背后的逻辑。

注意1:属性只在new-style类中起作用。
注意2:尽管功能方法对于类似缓存的负面影响比较小,但还是要尽量避免。
注意3:属性标记会让调用者认为开销(相当的)小,避免用属性做开销大的计算。

  • 如果你的类打算用来继承的话,并且这个类里有不希望子类使用的属性,就要考虑使用双下划线前缀并且没有后缀下划线的命名方式。这会调用Python的命名转换算法,将类的名字加入到属性名里。这样做可以帮助避免在子类中不小心包含了相同的属性名而产生的冲突。

注意1:只有类名才会整合进属性名,如果子类的属性名和类名和父类都相同,那么你还是会有命名冲突的问题。
注意2:命名转换会在某些场景使用起来不太方便,例如调试,getattr()。然而命名转换的算法有很好的文档说明并且很好操作。
注意3:不是所有人都喜欢命名转换。尽量避免意外的名字冲突和潜在的高级调用。

发表评论