PEP 8 —Python代码风格指南(一)

时间:2021-09-11 07:44:55

原文:PEP 8 – Style Guide for Python Code

PEP:8
题目:Python代码风格指南
作者:Guido van Rossum, Barry Warsaw , Nick Coghlan
状态:Active
类型:Process
创建:2001-07-05
往期:2001-07-05,2013-08-01

内容:

介绍

该文档提供了Python编程中的一些惯例,包含Python发布版中的一些基础库。 请参阅Python的C语言实现中的C代码配套信息PEP描述风格指南[1]。
本文档和PEP 257由Guido的原始Python风格指导文章改编,其中一些添加了Barry的风格指南的内容[2]。
这个风格指南会随着时间的推移而演变,而过去的惯例会因语言本身的变化而过时。
在很多工程中有自己的编程风格指导。如果工程内的风格指南与本文冲突的话,前者有优先级应该更高。

呆板的坚持一致性简直傻的没边了

(这个PEP8还是挺有情怀的,标题的英文是《A foolish consistency is the hobgoblin of little minds》,出自爱默生的self reliance

Guido的主要见解之一是我们大部分时间是在读代码而不是写。 这里提供的指导旨在提高代码的可读性,使其在广泛的Python代码中保持一致。就像PEP 20所说的:强调可读性
风格指南是关于一致性的。风格指南的一致性很重要。 一个项目的一致性更重要。 一个模块或功能内的一致性是最重要的。
但是,我们应该知道何时需要不一致 - 有时风格指南的建议不适用。 如果由此疑惑的话,应当遵从自己的判断。或者你可以看看其他的例子,决定什么看起来最好。并且不要犹豫去问问题!
特别是:不要只是为了符合这个PEP 8建议而破坏向后兼容性!

下面有些其他的原因可以让我们忽略特定指导原则:
1.当使用了这个指南导致代码可读性很差,甚至是使用过PEP 8的人去读依旧很差。
2.为了与原有的代码风格保持一致,也可以不遵循PEP 8(可能是出于历史原因),当然还有一种可能是原有代码的风格是乱的,这样的话也可以趁着这个机会整理一下之前混乱的风格。
3.代码风格问题出现的比指南还要早,而且已经没有什么必要再修改。
4.当代码需要与不支持风格指南推荐功能的旧版本的Python保持兼容时。

代码的布局

缩进

一次缩进使用4个空格
连续的行应使用Python的内隐行以垂直对齐的方式连接在圆括号、方括号或花括号内,或者使用悬挂式缩进[7]来将封装的元素对齐。在使用悬挂式缩进时,应注意下面的问题:在第一行中不应该有任何参数(悬挂式缩进时),并且应该使用进一步的缩进来清楚表示参数的延续线。

正确的示范:

#在分界处对齐,var_one与var_three对齐
foo = long_function_name(var_one, var_two,
var_three, var_four)

#使用更多的缩进与其他部分区分开,var_one和var_four与print()缩进不同
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)

# 参数的缩进与print()函数缩进相同
def long_function_name(
var_one, var_two, var_three,
var_four)
:

print(var_one)

对于连续的行而言,四个空格的规定不是必须的。

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

如果if语句的条件长到需要多行才能写下,值得注意的是,在多行条件语句中,左括号加空格再加上两个字符关键字的组合的形式会为多行条件的后续行创建一个自然的4空格缩进。这种形式很像if中的嵌套,这样就带来了视觉上的混淆。这个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()

(另外可以参考下面的考虑:关于换行符应在二元运算符之前还是之后?)

在Python的List数据结构中,右面的(closing brace/opening brace)花括号/方括号/圆括号可以在列表的最后一行的第一个非空格字符之下排列,如下:

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

用Tab还是空格?

推荐使用空格作为缩进方式。
Tab只有在之前就已经使用了Tab作为缩进的代码中继续使用。
Python 3中禁止缩进时空格和Tab混合使用。
Python 2中混合使用了空格和Tab的代码最好应该改成只是用Tab。
当使用-t选项调用Python 2命令行解释器时,会发出关于非法混合Tab和空格的代码的警告。当使用-tt调用时,这些警告会变成错误。 强烈推荐使用这些选项!

行的最大长度

所有行的最大值为79个字节。
对于长度较短的文本块(文本输入或注释)较少的结构限制,行长度应限制为72个字符。

限制所需的编辑器窗口宽度使得可以并排打开多个文件,并且在使用在相邻列中呈现两个版本的代码审阅工具时可以正常工作。

大多数工具中的默认包装会破坏代码的可视化结构,使其更难于理解。选择限制以避免将窗口宽度设置为80的编辑器中包围,即使工具在包装线时将标记字形放在最后一列中。一些基于Web的工具根本不能提供动态的线条包装。

一些团队强烈希望更长的线路长度。对于唯一或主要由能够就此问题达成协议的团队维护的代码,可以将标称行长度从80增加到100个字符(有效地将最大长度增加到99个字符),前提是注释和文档包装仍然包装72个字符

Python标准库是保守的,并且需要将限制行限制为79个字符(和docstrings /注释到72)。

换行长行的首选方法是使用括号,括号和大括号内的Python隐含行延续。通过将表达式包含在括号中,可以在多行上分割长行。这些应优先于使用反斜杠进行行连续使用。

有时,反斜杠可能仍然适用。例如,long,multiple with-statements不能使用隐式继承,所以反斜杠是可以接受的:

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

几十年来,都是推崇在二元运算符之后换行的风格。但这可能会在两个方面损害程序的可读性:程序员不得不将视线分散到屏幕上的不同的行,并且程序员还需要把视线从找到的操作数上移到上一行上。 如此,眼睛必须做额外的工作来判断哪些项目被添加,哪些被减去:

# 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代码中,在运算符前面断开是合法(就是说这个你的Python版本这样写不报错),但是要和你之前的风格一致。而对于新的代码,建议使用克努特推荐的风格(在前面断开)。

空行

在开头定义的功能和类,带行空行。

类中定义的方法首位单行空行。

可以使用额外的空行(谨慎地)来分离相关功能组。在一系列相关的单线程程序里(例如,一组虚拟实现),不应该使用空行。

在功能函数中使用空白行,应当谨慎地指示逻辑部分。

Python接受ctrl-L的形式的作为空格; 许多工具将这些字符视为页面分隔符,因此您可以使用它们来分隔文件的相关部分的页面。 请注意,一些编辑器和基于Web的代码查看器可能不支持ctrl-L,并会在其位置显示另一个字形。

源文件的编码

核心Python发行版中的代码应始终使用UTF-8(或Python 2中的ASCII)。

使用ASCII(Python 2)或UTF-8(在Python 3中)的文件不应该具有编码声明。

在标准库中,非默认编码应仅用于测试,注释或者评论和文档中用以提及作者的名字。
docstring需要提及包含非ASCII字符的作者名称时;否则,使用\ x,\ u,\ U或\ N转义是在字符串文字中包含非ASCII数据的首选方法。

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

鼓励有全球观众的开源项目采取类似的政策。

引入

引入应当分行:

Yes: import os
import sys
No:  import sys, os

这样是可以的:

from subprocess import Popen, PIPE

import通常在文件的开头位置,仅仅在模块的注释、文档字符串说明、全局变量和常量之后

引入应按以下顺序分组:

1.标准库
2.相关第三方库
3.自定义库
您应该在每组导入之间留一个空行。

推荐绝对引入的方式,它们通常可读性更好。如果导入系统配置不正确,至少可以提供更准确的错误消息。(例如,当一个包中的目录在sys.path中最终出现时)

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.MyClass”
“foo.bar.yourclass.YourClass”

应避免使用通配符导入(from <module> import *),因为它们使在名称空间中的不清楚,从而使不管是读者还是许多自动化编译工具都很困惑。通配符导入有一个防御用例,它是作为公共API的一部分重新发布一个内部接口 (例如,覆盖来自可选加速器模块的定义的界面的纯Python实现,以及将覆盖哪些定义是未知的)。

当以这种方式重新发布名称时,下面关于公共和内部接口的指南仍然适用。

模块的双下划线命名

具有两个前导和后下划线的模块de 名称(如__all____author____version__等)应放置在模块的字符串说明之后,import操作之前。(除了__future__
Python要求未来的导入必须出现在的所有其他引入代码之后(除了字符串说明)。

例如:

"""
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