ImQi1 / 技术 / PEP8,Python开发的美工指导员
封面

PEP8,Python开发的美工指导员

Python编程语言的官方代码风格指南

我最早了解PEP8时是在PyCharm的格式提醒中看到的。那时候初学Python语言,只知道Python由严格的缩进级别控制,也只是跟着老师敲代码的步伐一步一步模仿。我是在《Python从入门到精通》这本书上接触到Python。那时前几课的老师都是一个女老师。她那时候只是在IDLE上教如何敲代码,但是她的代码给人的感觉就是很工整,即使是纯手敲。因为她就遵从了PEP8规范之一——“#”注释要在该行代码最后一个字符后用两个空格隔开,且注释内容在“#”之后用一个空格隔开,即a = 1 # 注释xxx。代码的可读性的重要性不言而喻,而每个人对代码的工整程度都有不同的定义。因此kennethreitz就总结出了一套Python代码编写规范。此规范目前被大众所认可,也被广泛学习。PEP8官网为pep8.org,此文章会挑选此网站中比较常用的规范,分享给大家。

代码布局

缩进

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

连续行应该使用Python在圆括号、方括号和大括号内的隐式行连接或使用悬挂缩进。垂直对齐包装的元素当使用悬挂缩进时,应考虑以下事项:第一行不应有任何参数,并且应使用进一步的缩进以清楚地将其本身区分为继续行。

当函数参数列表过长而需要换行时,换到下一行的参数应与上一行的参数保持垂直对齐。若选择在第一个参数前就换行,则第一个参数前应缩进两个级别,以和参数下一行的代码缩进区分开。

正确的缩进:

# Aligned with opening delimiter.
foo = long_function_name(var_one, var_two,
                         var_three, var_four)

# More indentation included to distinguish this from the rest.
def long_function_name(
        var_one, var_two, var_three,
        var_four):
    print(var_one)

# Hanging indents should add a level.
foo = long_function_name(
    var_one, var_two,
    var_three, var_four)

错误的缩进:

# Arguments on first line forbidden when not using vertical alignment.
foo = long_function_name(var_one, var_two,
    var_three, var_four)

# Further indentation required as indentation is not distinguishable.
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),加上一个空格,加上一个左括号,为多行条件的后续行创建了一个自然的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',
)

选择空格还是选择制表符

空格是首选的缩进方法。

Python 3不允许在缩进中混合使用制表符和空格。

最大行长度

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

对于具有较少结构限制(文档字符串或注释)的长文本块,行长度应限制为72个字符。

限制所需的编辑器窗口宽度可以使多个文件并排打开,并且在使用代码评审工具在相邻列中显示两个版本时效果很好。

大多数工具中的默认包装会破坏代码的可视结构,使其更难理解。选择这些限制是为了避免在窗口宽度设置为80的编辑器中换行,即使换行时工具在最后一列中放置标记图示符。一些基于web的工具可能根本不提供动态换行。

一些团队强烈喜欢更长的线长度。对于专门或主要由能够就此问题达成一致的团队维护的代码,可以将标称行长度从80个字符增加到100个字符(有效地将最大长度增加到99个字符),前提是注释和文档字符串仍然以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())

二元运算符在何处打破

在Python代码中,允许在二元运算符之前或之后中断,只要约定在本地保持一致。对于新的代码,建议遵循Knuth的风格,即在运算符前打断。

# Yes: easy to match operators with operands
income = (gross_wages
          + taxable_interest
          + (dividends - qualified_dividends)
          - ira_deduction
          - student_loan_interest)

空白行

用两个空行包围顶级函数和类定义。

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

额外的空白行可以用来(有节制地)分隔相关函数的组。在一堆相关的一行代码之间可以省略空行(例如一组虚拟实现)。

在函数中尽量少用空行来表示逻辑部分。

import导入

若有多个导入则应该分行。

正确:

import os
import sys

错误:

import os, sys

但限定导入可以放在一行。

正确:

from subprocess import Popen, PIPE

导入总是放在文件的顶部,就在所有模块注释和文档字符串之后,在模块全局值和常量之前。

导入应按以下顺序分组:

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

您应该在每组导入之间放置一个空行。

推荐使用绝对导入,因为它们可以使变量具有更好的可读性,也会有更少的错误提示。

import mypkg.sibling
from mypkg import sibling
from mypkg.sibling import example

若显式绝对导入过于冗余,可选用显式相对导入。

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.MyClassfoo.bar.yourclass.YourClass

应该避免使用通配符导入(from import *),因为它们会使命名空间中出现的名称不清楚,从而使读者和许多自动化工具感到困惑。通配符导入有一个可辩护的用例,即将内部接口重新发布为公共API的一部分(例如,使用来自可选加速器模块的定义覆盖接口的纯Python实现,并且事先不知道哪些定义将被覆盖)。

表达式和语句中的空格

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

直接在圆括号、方括号或大括号内:

正确:

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

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

正确:

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]

紧接在开始函数调用参数列表的左括号之前:

正确:

spam(1)

错误:

spam (1)

紧接在开始索引或切片的左括号之前:

错误:

dct['key'] = lst[index]

错误:

dct ['key'] = lst [index]

赋值(或其他)运算符周围有多个空格,用于将其与另一个运算符对齐。

正确:

x = 1
y = 2
long_variable = 3

错误:

x             = 1
y             = 2
long_variable = 3

其他空格

避免在任何地方尾随空白。因为它通常是不可见的,所以可能会令人困惑:例如,后面跟有空格和换行符的反斜杠不算作行继续标记。

始终在这些二元运算符的两侧使用单个空格:赋值(=)、增广赋值(+=、-=等)、比较(==、<、>、!=、<>、<=、>=、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)

如果使用具有不同优先级的运算符,请考虑在具有最低优先级的运算符周围添加空白。用你自己的判断;但是,永远不要使用一个以上的空格,并且始终在二元运算符的两边具有相同数量的空白。

正确:

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)

当用于指示关键字参数或默认参数值时,不要在=符号周围使用空格。

正确:

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

错误:

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

函数注释应该使用冒号的常规规则,并且在->箭头周围总是有空格(如果存在)。

正确:

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

错误:

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

将参数注释与默认值组合时,在=符号周围使用空格(但仅适用于既有注释又有默认值的参数)。

正确:

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

复合语句(同一行上的多个语句)通常不受欢迎。

正确:

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

注释

内联注释是与语句位于同一行的注释。内联注释应与语句至少用两个空格分隔。它们应该以#和一个空格开头。

内联注释是不必要的,事实上,如果它们陈述了显而易见的东西,就会分散注意力。

这样做毫无意义:

i = i + 1  # 将i的值增1

注释应该多使用文档字符串。有关更多文档字符串的知识请阅读下面的文章。

{cat_insidepost id="216"}

命名

命名规则

有以下几种常见的命名:

  • b (单个小写字母)
  • B (单个大写字母)
  • lowercase(小写英文单词)
  • lower_case_with_underscores(带下画线的小写单词)
  • UPPERCASE(全部大写)
  • UPPER_CASE_WITH_UNDERSCORES(带下画线的大写单词)
  • CapitalizedWords (or CapWords, CamelCase5, StudlyCaps)(驼峰命名法)
  • mixedCase (和 CapitalizedWords 区分开)
  • Capitalized_Words_With_Underscores (小丑命名)

在CapWords中使用缩写时,请将缩写的所有字母大写。HTTPServerError比HttpServerError好。

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

此外,还可以识别以下使用前导或尾随下划线的特殊形式(这些形式通常可以与任何大小写约定组合使用):

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

single_trailing_underscore_:约定使用,避免与Python关键字冲突,例如:def f(class_=None)

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

类名

类名通常应使用CapWords约定。

在接口被记录并主要用作可调用的情况下,可以使用函数的命名约定。

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

设计用于通过from M import *使用的模块应该使用__all__机制来防止导出全局变量,或者使用旧的约定,即在这些全局变量的前缀加上下划线(您可能希望这样做以表明这些全局变量是“模块非公共的”)。

函数名

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

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

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

始终使用cls作为类方法的第一个参数。

如果函数参数的名称与保留关键字冲突,通常最好在后面附加一个下划线,而不是使用缩写或拼写错误。class_比clss好。(也许更好的做法是使用同义词来避免这种冲突。

常量

常量通常在模块级别上定义,并以大写字母书写,下划线分隔单词。示例包括MAX_OVERFLOW和TOTAL。

其他规范

像None这样的单例比较应该总是用is或is not来完成,而不是相等运算符。

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

使用is not运算符而不是not ... is。虽然这两种表达在功能上相同,但前者更易于阅读,更推荐。

正确:

if foo is not None:

错误:

if not foo is None:

当实现具有丰富比较的排序操作时,最好实现所有六个操作运算符的魔法函数(__eq__、__ne__、__lt__、__le__、__gt__、__ge__),而不是依赖于其他代码来执行特定的比较。有关魔法函数的文章请阅读下文。

{cat_insidepost id="141"}

从Exception而不是BaseException导出异常。从BaseException的直接继承是为异常保留的,因为捕获它们几乎总是错误的。

根据捕获异常的代码可能需要的区别来设计异常层次结构,而不是根据引发异常的位置。目的是回答问题“哪里出错了?

一个空的except:子句将捕获SystemExit和KeyboardInterrupt异常,这使得使用Control-C中断程序变得更加困难,并且可以掩盖其他问题。如果你想捕获所有的异常,可以使用except Exception:(bare except相当于except BaseException:)。

一个好的经验法则是将裸“except”子句的使用限制在两种情况下:

  1. 如果异常处理程序将打印或记录跟踪;至少用户将意识到错误已经发生。
  2. 如果代码需要做一些清理工作,但随后让异常向上传播raise。try...finally可以更好地处理这个问题。

上下文管理器在执行获取和释放资源以外的操作时,都应该通过单独的函数或方法调用。举例来说:

正确:

with conn.begin_transaction():
    do_stuff_in_transaction(conn)

错误:

with conn:
    do_stuff_in_transaction(conn)

评论

欢迎评论,优秀的评论博主会回评,也可留下站点方便博主回访。