目录 | 描述 |
---|---|
PEP | 8 |
标题 | python代码样式指南 |
作者 | Guido van Rossum(guido at python.org), Barry Warsaw(barry at python.org), Nick Coghlan(ncoghlan at gmail.com) |
状态 | Active |
创建时间 | 2001年7月5日 |
本文档给出了Python代码的编码约定,该Python代码包含主Python发行版中的标准库。请参阅随附的信息性PEP,该PEP描述了Python2的C实现中C代码的样式准则。
本文档和PEP 257(Docstring约定)是从Guido最初的Python样式指南文章中改编而来,并对Barry的样式指南3进行了一些补充。
该样式指南会随着时间的流逝而发展,因为会确定其他约定,而过去的约定会因语言本身的更改而过时。
许多项目都有自己的编码风格准则。如有任何冲突,此类项目特定的指南优先于该项目。
Guido的主要见解之一是代码的读取比编写的次数要多
。此处提供的指南旨在提高代码的可读性,并使其在各种Python代码中保持一致。正如PEP 20所说,“可读性至关重要”。
样式指南是关于一致性的。与该样式指南的一致性很重要。项目内的一致性更为重要。一个模块或功能内的一致性是最重要的。
但是,要知道什么时候不一致-有时样式指南的建议就不适用。如有疑问,请运用最佳判断。查看其他示例并确定最合适的方法。不要犹豫,问!
特别是:不要仅仅为了遵守本PEP而向后兼容!
忽略特定准则的其他一些好的理由:
每个缩进层使用4个空格。
悬挂式缩进
。使用悬挂式缩进时,应考虑以下几点:第一行不应该有任何参数,应该使用进一步的缩进来清楚地将其本身作为延续行区分开来。正例:
# 与左括号对齐,PyCharm默认
foo = long_function_name(var_one, var_two,
var_three, var_four)
# 添加4个空格(额外的缩进级别)以将参数与其余参数区分开。PyCharm默认
def long_function_name(
var_one, var_two, var_three,
var_four):
print(var_one)
# 悬挂的缩进应添加一个级别。PyCharm默认
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空格规则是可选的。
可选的:
# 悬挂缩进*可以*除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()
# 在条件延续行上添加一些额外的缩进。PyCharm默认
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',
)
也可以将其排列在开始多行构造的行的第一个字符之下,如:
正例:
# PyCharm默认
my_list = [
1, 2, 3,
4, 5, 6,
]
result = some_function_that_takes_arguments(
'a', 'b', 'c',
'd', 'e', 'f',
)
空格是首选的缩进方法。
-t
选项调用Python 2命令行解释器时,它会发出关于非法混合制表符和空格的代码的警告。当使用-tt
时,这些警告将变成错误。强烈推荐这些选项!将所有行限制为最多79个字符。
正例:
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())
请参阅前面有关多行if语句的讨论,以获取有关缩进此类多行带状态语句的进一步思考。
另一种此类情况是使用assert语句。
确保续行进行适当缩进。
反例:
# 操作符远离它们的操作数
income = (gross_wages +
taxable_interest +
(dividends - qualified_dividends) -
ira_deduction -
student_loan_interest)
二元运算符之前断开
。” 4正例:
# 容易匹配操作符和操作数
income = (gross_wages
+ taxable_interest
+ (dividends - qualified_dividends)
- ira_deduction
- student_loan_interest)
在Python代码中,允许在二元运算符之前或之后中断,只要本地的约定是一致的。对于新代码,建议使用Knuth的样式。
两个空行
包围顶级函数和类定义。单个空白行
包围。正例:
import os
import sys
反例:
import sys, os
但是可以这样:
正例:
from subprocess import Popen, PIPE
文件的顶部
,在模块的注释和文档字符串之后
,在模块的全局变量和常量之前
。导入包操作,应按以下顺序分组:
- 标准库导入。
- 相关第三方进口。
- 本地应用程序/库特定的导入。
- 应在每组导入之间放置一个空白行。
推荐使用绝对路径导入
,如果导入系统配置不正确(比如程序包中的一个目录在sys.path里的路径后),使用绝对路径导入会更具可读性,并且性能更好(至少会提供更好的错误信息):正例:
import mypkg.sibling
from mypkg import sibling
from mypkg.sibling import example
正例:
from . import sibling
from .sibling import example
正例:
from myclass import MyClass
from foo.bar.yourclass import YourClass
正例:
import myclass
import foo.bar.yourclass
使用时:
myclass.MyClass
foo.bar.yourclass.YourClass
from import *
),因为通配符的使用会导致不清楚命名空间中存在哪些名字,这会使得读取接口和许多自动化工具之间产生混淆。 对于通配符的导入,有一个合理的用例,即将内部接口重新发布为公共API的一部分(例如,用可选的加速器模块中的定义覆盖纯Python实现的接口,以及重写那些事先不知道的定义)。__all __
,__ author __
,__ version__
等的模块级别“呆名”(即带有两个前导下划线和两个下划线的名称)应放置在模块文档字符串之后,但应放在除from __future__
导入之外的任何import语句之前。 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
单引号字符串和双引号字符串是相同的
。 本PEP对此不做任何建议。 选择一条规则并坚持下去。 但是,当字符串包含单引号或双引号字符时,请使用另一个以避免在字符串中使用反斜杠。 它提高了可读性。在以下情况下避免不必要的空格:
正例: 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
正例:
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 munge(input: AnyStr): ...
def munge() -> PosInt: ...
反例:
def munge(input:AnyStr): ...
def munge()->PosInt: ...
正例:
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(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()
正例:
FILES = ('setup.cfg',)
反例:
FILES = 'setup.cfg',
正例:
FILES = [
'setup.cfg',
'tox.ini',
]
initialize(FILES,
error=True,
)
反例:
FILES = ['setup.cfg', 'tox.ini',]
initialize(FILES, error=True,)
反例:
x = x + 1 # Increment x
但是有时候,这很有用:
正例:
x = x + 1 # Compensate for border
"""
应自成一行:
"""Return a foobang
Optional plotz says to frobnicate the bizbaz first.
"""
"""
在同一行。Python库的命名约定有点混乱,因此我们永远都无法做到完全一致-尽管如此,这是当前推荐的命名标准。 新的模块和软件包(包括第三方框架)应按照这些标准编写,但是如果现有库具有不同的样式,则首选内部一致性。
那些暴露给用户的API接口的命名,应该遵循反映使用场景
而不是实现的原则。
注意:
当在首字母大写的风格中用到缩写时,所有缩写的字母用大写,因此,HTTPServerError 比 HttpServerError 好。_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__:
(双下划线开头,双下划线结尾)“magic”对象或者存在于用户控制的命名空间内的属性,例如:__init__
,__import__
或者__file__
。除了作为文档之外,永远不要命这样的名。
from typing import TypeVar
VT_co = TypeVar('VT_co', covariant=True)
KT_contra = TypeVar('KT_contra', contravariant=True)
注意:
关于__name的使用存在一些争议(请参见下文)。__all__
属性在其公共API中显式声明名称。将__all__
设置为空列表表示该模块没有公共API。__all__
,内部接口(包,模块,类,函数,属性或其他名称)仍应以单个下划线作为前缀。__init__
模块。正例:
if foo is not None:
反例:
if not foo is None:
__eq__
,__ne__
, __lt__
, __gt__
, __ge__
)而不是依靠其他的代码去实现特定的比较。正例:
def f(x): return 2*x
反例:
f = lambda x: 2*x
第一个形式意味着生成的函数对象的名称是“f”而不是泛型“< lambda >”。
这在回溯和字符串显示的时候更有用。赋值语句的使用消除了lambda表达式优于显式def表达式的唯一优势
(即lambda表达式可以内嵌到更大的表达式中)。
正例:
try:
import platform_specific_module
except ImportError:
platform_specific_module = None
如果只有一个except: 块将会捕获到SystemExit和KeyboardInterrupt异常,
这样会很难通过Control-C中断程序,而且会掩盖掉其他问题。如果你想捕获所有指示程序出错的异常,
使用 except Exception: (只有except等价于 except BaseException:)。
两种情况不应该只使用‘excpet’块:
1. 如果异常处理的代码会打印或者记录log;至少让用户知道发生了一个错误。
2. 如果代码需要做清理工作,使用 raise..try…finally 能很好处理这种情况并且能让异常继续上浮。
正例:
try:
process_data()
except Exception as exc:
raise DataProcessingFailedError(str(exc))
为了避免和原来基于逗号分隔的语法出现歧义,Python3只支持这一种语法。
正例:
try:
value = collection[key]
except KeyError:
return key_not_found(key)
else:
return handle_value(value)
反例:
try:
# Too broad!
return handle_value(collection[key])
except KeyError:
# Will also catch KeyError raised by handle_value()
return key_not_found(key)
正例:
with conn.begin_transaction():
do_stuff_in_transaction(conn)
反例:
with conn:
do_stuff_in_transaction(conn)
第二个例子没有提供任何信息去指明__enter__和__exit__方法在事务之后做出了关闭连接之外的其他事情。
这种情况下,明确指明非常重要。
正例:
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)
正例: if foo.startswith('bar'):
反例: if foo[:3] == 'bar':
正例: if isinstance(obj, int):
反例: if type(obj) is type(1):
if isinstance(obj, basestring):
注意,在Python3中,unicode和basestring都不存在了(只有str)
并且bytes类型的对象不再是string类型的一种(它是整数序列)
正例:
if not seq:
if seq:
反例:
if len(seq):
if not len(seq):
正例: if greeting:
反例: if greeting == True:
更糟: if greeting is True:
反例:
def foo():
try:
1 / 0
finally:
return 42
随着PEP 484的引入,功能型注释的风格规范有些变化。
# type: ignore
这会告诉检查器忽略所有的注释。(在 PEP 484中可以找到从类型检查器禁用投诉的更细粒度的方法。)
正例:
code: int
class Point:
coords: Tuple[int, int]
label: str = ''
反例:
code:int # No space after colon
code : int # Space before colon
class Test:
result: int=0 # No spaces around equality sign
https://www.python.org/dev/peps/pep-0008/ ↩︎
PEP 7, Style Guide for C Code, van Rossum ↩︎
Barry's GNU Mailman style guide http://barry.warsaw.us/software/STYLEGUIDE.txt ↩︎
Donald Knuth's The TeXBook, pages 195 and 196. ↩︎
Typeshed repo https://github.com/python/typeshed ↩︎
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 ↩︎