#PEP 8 – Python编码风格指南python
PEP: | 8 |
---|---|
Title: | Style Guide for Python Code |
Author: | Guido van Rossum , Barry Warsaw , Nick Coghlan |
Status: | Active |
Type: | Process |
Created: | 05-Jul-2001 |
Post-History: | 05-Jul-2001, 01-Aug-2013 |
这篇文档说明了Python主要发行版中标准库代码所遵照的规范。对于Python的C语言实现中的编码规范,请参考实现Python的C代码风格指南PEP 7。git
这篇文档和PEP 257(Docstring约定)都改编自Guido1最先的Python风格指南文章,而且添加了一些Barry的风格指南2中的内容。程序员
语言自身在发生着改变,随着新的规范的出现和旧规范的过期,代码风格也会随着时间演变。github
不少项目都有本身的一套风格指南。若和本指南有任何冲突,应该优先考虑其项目相关的那套指南。web
Guido的一个重要观点是代码被读的次数远多于被写的次数。这篇指南旨在提升代码的可读性,使浩瀚如烟的Python代码风格能保持一致。正如PEP 20那首《Zen of Python》的小诗里所说的:“可读性很重要(Readability counts)”。算法
这本风格指南是关于一致性的。同风格指南保持一致性是重要的,可是同项目保持一致性更加剧要,同一个模块和一个函数保持一致性则最为重要。express
然而最最重要的是:要知道什么时候去违反一致性,由于有时风格指南并不适用。当存有疑虑时,请自行作出最佳判断。请参考别的例子去作出最好的决定。而且不要犹豫,尽管提问。编程
特别的:千万不要为了遵照这篇PEP而破坏向后兼容性!api
若是有如下借口,则能够忽略这份风格指南:缓存
当采用风格指南时会让代码更难读,甚至对于习惯阅读遵循这篇PEP的代码的人来讲也是如此。
须要和周围的代码保持一致性,但这些代码违反了指南中的风格(但是时历史缘由形成的)——尽管这可能也是一个收拾别人烂摊子的机会(进入真正的极限编程状态)。
如果有问题的某段代码早于引入指南的时间,那么没有必要去修改这段代码。
代码须要和更旧版本的Python保持兼容,而旧版本的Python不支持风格指南所推荐的特性。
每一个缩进级别采用4个空格。
连续行所包装的元素应该要么采用Python隐式续行,即垂直对齐于圆括号、方括号和花括号,要么采用悬挂缩进(hanging indent)4。采用悬挂缩进时需考虑如下两点:第一行不该该包括参数,而且在续行中须要再缩进一级以便清楚表示。
正确的例子:
# 同开始分界符(左括号)对齐 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空格的规则能够不遵照。
一样可行的例子:
# 悬挂缩进能够不采用4空格的缩进方法。 foo = long_function_name( var_one, var_two, var_three, var_four)
若是if
语句太长,须要用多行书写,2个字符(例如,if
)加上一个空格和一个左括号恰好是4空格的缩进,但这对多行条件语句的续行是没用的。由于这会和if
语句中嵌套的其余的缩进的语句产生视觉上的冲突。这份PEP中并无作出明确的说明应该怎样来区分条件语句和if
语句中所嵌套的语句。如下几种方法都是可行的,但不只仅只限于这几种方法:
# 不采用额外缩进 if (this_is_one_thing and that_is_another_thing): do_something() # 增长一行注释,在编辑器中显示时能有所区分 # supporting syntax highlighting. 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', )
###Tab仍是空格?(Tab Or Space?)
推荐使用空格来进行缩进。
Tab应该只在现有代码已经使用tab进行缩进的状况下使用,以便和现有代码保持一致。
Python 3不容许tab和空格混合使用。
Python 2的代码如有tab和空格混合使用的状况,应该把tab所有转换为只有空格。
当使用命令行运行Python 2时,使用-t
选项,会出现非法混用tab和空格的警告。当使用-tt
选项时,这些警告会变成错误。强烈推荐使用这些选项!
将全部行都限制在79个字符长度之内。
对于连续大段的文字(好比文档字符串(docstring)或注释),其结构上的限制更少,这些行应该被限制在72个字符长度内。
限制编辑器的窗口宽度能让好几个文件同时打开在屏幕上显示,在使用代码评审(code review)工具时在两个相邻窗口显示两个版本的代码效果很好。
不少工具的默认自动换行会破坏代码的结构,使代码更难以理解。在窗口大小设置为80个字符的编辑器中,即便在换行时编辑器可能会在最后一列放置一个记号,为避免自动换行也须要限制每行字符长度。一些基于web的工具可能根本没有自动换行的功能。
一些团队会强烈但愿行长度比79个字符更长。当代码仅仅只由一个团队维护时,能够达成一致让行长度增长到80到100字符(实际上最大行长是99字符),注释和文档字符串仍然是以72字符换行。
Python标准库比较传统,将行长限制在79个字符之内(文档字符串/注释为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())
(参照前面关于 多行if语句的讨论来进一步考虑这里with
语句的缩进。)
另外一个这样的例子是assert
语句。
要确保续行的缩进适当。
长期以来一直推荐的风格是在二元运算符以后换行。可是这样会影响代码可读性,包括两个方面:一是运算符会分散在屏幕上的不一样列上,二是每一个运算符会留在前一行并远离操做数。因此,阅读代码的时候眼睛必须作更多的工做来肯定哪些操做数被加,哪些操做数被减:
# 错误的例子:运算符远离操做数 income = (gross_wages + taxable_interest + (dividends - qualified_dividends) - ira_deduction - student_loan_interest)
为了解决这个可读性问题,数学家及其出版商遵循相反的规定。Donald Knuth在他的“电脑和排版”系列中解释了传统的规则:“尽管在段落中的公式老是在二元运算符以后换行,但显示公式时老是在二元运算符以前换行”5。
# 正确的例子:更容易匹配运算符与操做数 income = (gross_wages + taxable_interest + (dividends - qualified_dividends) - ira_deduction - student_loan_interest)
在Python代码中,只要在统一项目中约定一致,就能够在二元运算符以前或以后换行。对于新编写的代码,建议使用Knuth的风格。
使用2个空行来分隔最外层的函数(function)和类(class)定义。
使用1个空行来分隔类中的方法(method)定义。
可使用额外的空行(尽可能少)来分隔一组相关的函数。在一系列相关的仅占一行的函数之间,空行也能够被省略(好比一组虚函数定义)。
在函数内使用空行(尽可能少)使代码逻辑更清晰。
Python支持control-L(如:^L)换页符做为空格;许多工具将这些符号做为分页符,所以你可使用这些符号来分页或者区分文件中的相关区域。注意,一些编辑器和基于web的代码预览器可能不会将control-L识别为分页符,而是显示成其余符号。
Python核心发行版中的代码应该一直使用UTF-8(Python 2中使用ASCII)。
使用ASCII(Python 2)或者UTF-8(Python 3)的文件不该该添加编码声明。
在标准库中,只有用做测试目的,或者注释或文档字符串须要说起做者名字而不得不使用非ASCII字符时,才能使用非默认的编码。不然,在字符串文字中包括非ASCII数据时,推荐使用\x
, \u
, \U
或\N
等转义符。
对于Python 3.0及其之后的版本中,标准库遵循如下原则(参见PEP 3131):Python标准库中的全部标识符都必须只采用ASCII编码的标识符,在可行的条件下也应当使用英文词(不少状况下,使用的缩写和技术术语词都不是英文)。此外,字符串文字和注释应该只包括ASCII编码。只有两种例外:
鼓励具备全球受众的开放源码项目采用相似的原则。
Imports应该分行写,而不是都写在一行,例如:
# 分开写 import os import sys # 不要像下面同样写在一行 import sys, os
这样写也是能够的:
from subprocess import Popen, PIPE
Imports应该写在代码文件的开头,位于模块(module)注释和文档字符串(docstring)以后,模块全局变量(globals)和常量(constants)声明以前。
Imports应该按照下面的顺序分组来写:
不一样组的imports以前用空格隔开。
推荐使用绝对(absolute)imports,由于这样一般更易读,在import系统没有正确配置(好比中的路径以sys.path
结束)的状况下,也会有更好的表现(或者至少会给出错误信息):
import mypkg.sibling from mypkg import sibling from mypkg.sibling import example
然而,除了绝对imports,显式的相对imports也是一种能够接受的替代方式。特别是当处理复杂的包布局(package layouts)时,采用绝对imports会显得啰嗦。
from . import sibling from .sibling import example
标准库代码应当一直使用绝对imports,避免复杂的包布局。
隐式的相对imports应该永不使用,而且Python 3中已经被去掉了。
当从一个包括类的模块中import一个类时,一般能够这样写:
from myclass import MyClass from foo.bar.yourclass import YourClass
若是和本地命名的拼写产生了冲突,应当直接import模块:
import myclass import foo.bar.yourclass
而后使用”myclass.MyClass”和”foo.bar.yourclass.YourClass”。
避免使用通配符imports(from <module> import *
),由于会形成在当前命名空间出现的命名含义不清晰,给读者和许多自动化工具形成困扰。有一个能够正当使用通配符import的情形,即将一个内部接口从新发布成公共API的一部分(好比,使用备选的加速模块中的定义去覆盖纯Python实现的接口,预先没法知晓具体哪些定义将被覆盖)。
当使用这种方式从新发布命名时,指南后面关于公共和内部接口的部分仍然适用。
模块中的“双下滑线”(变量名以两个下划线开头,两个下划线结尾)变量,好比__all__
,__author
,__version__
等,应该写在文档字符串(docstring)以后,除了form __future__
引用(imports)的任何其它类型的引用语句以前。Python要求模块中__future__
的导入必须出如今除文档字符串(docstring)以外的任何其余代码以前。
例如:
"""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
在Python中表示字符串时,无论用单引号仍是双引号都是同样的。可是不推荐将这两种方式看做同样而且混用。最好选择一种规则并坚持使用。当字符串中包含单引号时,采用双引号来表示字符串,反之也是同样,这样能够避免使用反斜杠,代码也更易读。
对于三引号表示的字符串,使用双引号字符来表示6,这样能够和PEP 257的文档字符串(docstring)规则保持一致。
在下列情形中避免使用过多的空白:
方括号,圆括号和花括号以后:
#正确的例子: spam(ham[1], {eggs: 2}) #错误的例子: spam( ham[ 1 ], { eggs: 2 } )
逗号,分号或冒号以前:
#正确的例子: 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]
在调用函数时传递参数list的括号以前:
#正确的例子: spam(1) #错误的例子: pam (1)
在索引和切片操做的左括号以前:
#正确的例子: dct['key'] = lst[index] #错误的例子: dct ['key'] = lst [index]
赋值(或其余)运算符周围使用多个空格来和其余语句对齐:
#正确的例子: x = 1 y = 2 long_variable = 3 #错误的例子: x = 1 y = 2 long_variable = 3
=
),增量赋值运算符(+=
, -=
etc.),比较运算符(==
, <
, >
, !=
, <>
, <=
, >=
, in
, not in
, is
, is not
),布尔运算符(and
, or
, not
)。若是使用了优先级不一样的运算符,则在优先级较低的操做符周围增长空白。请你自行判断,不过永远不要用超过1个空格,永远保持二元运算符两侧的空白数量同样。
#正确的例子: 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/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()
末尾逗号一般是可选的,除非在定义单元素元组(tuple)时是必需的(并且在Python 2中,它们具备print语句的语义)。为了清楚起见,建议使用括号(技术上来讲是冗余的)括起来。
#正确的例子: FILES = ('setup.cfg',) #也正确,但使人困惑: FILES = 'setup.cfg',
当使用版本控制系统时,在未来有可能扩展的列表末尾添加冗余的逗号是有好处的。具体的作法是将每个元素写在单独的一行,并在行尾添加逗号,右括号单独占一行。可是,与有括号在同一行的末尾元素后面加逗号是没有意义的(上述的单元素元组除外)。
#正确的例子: FILES = [ 'setup.cfg', 'tox.ini', ] initialize(FILES, error=True, ) #错误的例子: FILES = ['setup.cfg', 'tox.ini',] initialize(FILES, error=True,)
和代码矛盾的注释还不如没有。当代码有改动时,必定要优先更改注释使其保持最新。
注释应该是完整的多个句子。若是注释是一个短语或一个句子,其首字母应该大写,除非开头是一个以小写字母开头的标识符(永远不要更改标识符的大小写)。
若是注释很短,结束的句号能够被忽略。块注释一般由一段或几段完整的句子组成,每一个句子都应该以句号结束。
你应该在句尾的句号后再加上2个空格。
使用英文写做,参考Strunk和White的《The Elements of Style》
来自非英语国家的Python程序员们,请使用英文来写注释,除非你120%肯定你的代码永远不会被不懂你所用语言的人阅读到。
块注释通常写在对应代码以前,而且和对应代码有一样的缩进级别。块注释的每一行都应该以#
和一个空格开头(除非该文本是在注释内缩进对齐的)。
块注释中的段落应该用只含有单个#
的一行隔开。
尽可能少用行内注释。
行内注释是和代码语句写在一行内的注释。行内注释应该至少和代码语句之间有两个空格的间隔,而且以#
和一个空格开始。
行内注释一般不是必要的,在代码含义很明显时甚至会让人分心。请不要这样作:
x = x + 1 # x自加
但这样作是有用的:
x = x + 1 # 边界补偿
要知道如何写出好的文档字符串(docstring),请参考PEP 257。
全部的公共模块,函数,类和方法都应该有文档字符串。对于非公共方法,文档字符串不是必要的,但你应该留有注释说明该方法的功能,该注释应当出如今def
的下一行。
PEP 257描述了好的文档字符应该遵循的规则。其中最重要的是,多行文档字符串以单行"""
结尾,不能有其余字符,例如:
"""Return a foobang
Optional plotz says to frobnicate the bizbaz first.
"""
对于仅有一行的文档字符串,结尾处的"""
应该也写在这一行。
Python标准库的命名约定有一些混乱,所以咱们永远都没法保持一致。但现在仍然存在一些推荐的命名标准。新的模块和包(包括第三方框架)应该采用这些标准,但如果已经存在的包有另外一套风格的话,仍是应当与原有的风格保持内部一致。
对于用户可见的公共部分API,其命名应当表达出功能用途而不是其具体的实现细节。
存在不少不一样的命名风格,最好可以独立地从命名对象的用途认出采用了哪一种命名风格。
一般区分如下命名样式:
b
(单个小写字母)B
(单个大写字母)lowercase
(小写)lower_case_with_underscores
(带下划线小写)UPPERCASE
(大写)UPPER_CASE_WITH_UNDERSCORES
(带下划线大写)CapitalizedWords
(也叫作CapWords或者CamelCase – 由于单词首字母大写看起来很像驼峰)。也被称做StudlyCaps。
注意:当CapWords里包含缩写时,将缩写部分的字母都大写。HTTPServerError
比HttpServerError
要好。
mixedCase
(注意:和CapitalizedWords不一样在于其首字母小写!)Capitalized_Words_With_Underscores
(这种风格超丑!)也有风格使用简短惟一的前缀来表示一组相关的命名。这在Python中并不常见,但为了完整起见这里也捎带提一下。好比,os.stat()
函数返回一个tuple,其中的元素名本来为st_mode
,st-size
,st_mtime
等等。(这样作是为了强调和POSIX系统调用结构之间的关系,可让程序员更熟悉。)
X11库中的公共函数名都以X开头。在Python中这样的风格通常被认为是没必要要的,由于属性和方法名以前已经有了对象名的前缀,而函数名前也有了模块名的前缀。
此外,要区别如下划线开始或结尾的特殊形式(能够和其它的规则结合起来):
_single_leading_underscore
: 以单个下划线开头是”内部使用”的弱标志。 好比, from M import *
不会import下划线开头的对象。
single_trailing_underscore_
: 以单个下划线结尾用来避免和Python关键词产生冲突,例如:
Tkinter.Toplevel(master, class_='ClassName')
__double_leading_underscore
: 以双下划线开头的风格命名类属性表示触发命名修饰(在FooBar类中,__boo
命名会被修饰成_FooBar__boo
; 见下)。
__double_leading_and_trailing_underscore__
: 以双下划线开头和结尾的命名风格表示“魔术”对象或属性,存在于用户控制的命名空间(user-controlled namespaces)里(也就是说,这些命名已经存在,但一般须要用户覆写以实现用户所须要的功能)。 好比, __init__
, __import__
或 __file__
。请依照文档描述来使用这些命名,千万不要本身发明。
不要使用字符’l’(L的小写的字母),’O’(o大写的字母),或者’I’(i的大写的字母)来做为单个字符的变量名。
在一些字体中,这些字符和数字1和0没法区别开来。好比,当想使用’l’时,使用’L’代替。
标准库中使用的标识符必须与ASCII兼容(参见PEP 3131中的policy这一节) 。
模块命名应短小,且为全小写。若下划线能提升可读性,也能够在模块名中使用。Python包命名也应该短小,且为全小写,但不该使用下划线。
当使用C或C++写的扩展模块有相应的Python模块提供更高级的接口时(好比,更加面向对象),C/C++模块名如下划线开头(例如,_sociket
)。
类命名应该使用驼峰(CapWords)的命名约定。
当接口已有文档说明且主要是被用做调用时,也可使用函数的命名约定。
注意对于内建命名(builtin names)有一个特殊的约定:大部份内建名都是一个单词(或者两个一块儿使用的单词),驼峰(CapWords)的约定只对异常命名和内建常量使用。
PEP 484中引入的类型变量名称一般应使用简短的驼峰命名: T
,AnyStr
,Num
。 建议将后缀_co
或_contra
添加到用于声明相应的协变(covariant)和逆变(contravariant)的行为。例如:
from typing import TypeVar VT_co = TypeVar('VT_co', covariant=True) KT_contra = TypeVar('KT_contra', contravariant=True)
因为异常实际上也是类,所以类命名约定也适用与异常。不一样的是,若是异常其实是抛出错误的话,异常名前应该加上”Error”的前缀。
(在此以前,咱们先假定这些变量都仅在同一个模块内使用。)这些约定一样也适用于函数命名。
对于引用方式设计为from M import *
的模块,应该使用__all__
机制来避免import全局变量,或者采用下划线前缀的旧约定来命名全局变量,从而代表这些变量是“模块非公开的”。
函数命名应该都是小写,必要时使用下划线来提升可读性。
只有当已有代码风格已是混合大小写时(好比threading.py),为了保留向后兼容性才使用混合大小写。
实例方法的第一参数永远都是self
。
类方法的第一个参数永远都是cls
。
在函数参数名和保留关键字冲突时,相对于使用缩写或拼写简化,使用如下划线结尾的命名通常更好。好比,class_
比clss
更好。(或许使用同义词避免这样的冲突是更好的方式。)
使用函数命名的规则:小写单词,必要时使用下划线分开以提升可读性。
仅对于非公开方法和变量命名在开头使用一个下划线。
避免和子类的命名冲突,使用两个下划线开头来触发Python的命名修饰机制。
Python类名的命名修饰规则:若是类Foo有一个属性叫__a
,不能使用Foo.__a
的方式访问该变量。(有用户可能仍然坚持使用Foo._Foo__a
的方法访问。)通常来讲,两个下划线开头的命名方法仅用于避免与设计为子类的类中的属性名冲突。
注意:关于__names的使用也有一些争论(见下)。
常量一般是在模块级别定义的,使用所有大写并用下划线将单词分开。如:MAX_OVERFLOW
和TOTAL
。
记得永远区别类的方法和实例变量(属性)应该是公开的仍是非公开的。若是有疑虑的话,请选择非公开的;由于以后将非公开属性变为公开属性要容易些。
公开属性是那些你但愿和你定义的类无关的客户来使用的,而且确保不会出现向后不兼容的问题。非公开属性是那些不但愿被第三方使用的部分,你能够不用保证非公开属性不会变化或被移除。
咱们在这里没有使用“私有(private)”这个词,由于在Python里没有什么属性是真正私有的(这样设计省略了大量没必要要的工做)。
另外一类属性属于子类API的一部分(在其余语言中常常被称为”protected”)。一些类是为继承设计的,要么扩展要么修改类的部分行为。当设计这样的类时,须要谨慎明确地决定哪些属性是公开的,哪些属于子类API,哪些真的只会被你的基类调用。
请记住以上几点,下面是Python风格的指南:
公开属性不该该有开头下划线。
若是公开属性的名字和保留关键字有冲突,在你的属性名尾部加上一个下划线。这比采用缩写和简写更好。(然而,和这条规则冲突的是,‘cls’对任何变量和参数来讲都是一个更好地拼写,由于你们都知道这表示class,特别是在类方法的第一个参数里。)
注意 1:对于类方法,参考以前的参数命名建议。
对于简单的公共数据属性,最后仅公开属性名字,不要公开复杂的调用或设值方法。请记住,若是你发现一个简单的数据属性须要增长功能行为时,Python为功能加强提供了一个简单的途径。这种状况下,使用Properties
注解将功能实现隐藏在简单数据属性访问语法以后。
注意 1:Properties
注解仅仅对新风格类有用。
注意 2:尽可能保证功能行为没有反作用,尽管缓存这种反作用看上去并无什么大问题。
注意 3: 对计算量大的运算避免试用properties;属性的注解会让调用者相信访问的运算量是相对较小的。
若是你的类将被子类继承的话,你有一些属性并不想让子类访问,考虑将他们命名为两个下划线开头而且结尾处没有下划线。这样会触发Python命名修饰算法,类名会被修饰添加到属性名中。这样能够避免属性命名冲突,以避免子类会不经意间包含相同的命名。
注意 1:注意命名修饰仅仅是简单地将类名加入到修饰名中,因此若是子类有相同的类名合属性名,你可能仍然会遇到命名冲突问题。
注意 2:命名修饰能够有特定用途,好比在调试时,__getattr__()
比较不方便。然而命名修饰算法的能够很好地记录,而且容意手动执行。
注意 3:不是全部人都喜欢命名修饰。须要试着去平衡避免偶然命名冲突的需求和高级调用者使用的潜在可能性。
任何向后兼容性保证仅对公开接口适用。相应地,用户可以清楚分辨公开接口和内部接口是很重要的。
文档化的接口被认为是公开的,除非文档中明确申明了它们是临时的或者内部接口,不保证向后兼容性。全部文档中未提到的接口应该被认为是内部的。
为了更好审视公开接口和内部接口,模块应该在__all
属性中明确申明公开API是哪些。将__all__
设为空list表示该模块中没有公开API。
即便正确设置了__all
属性,内部接口(包,模块,类,函数,属性或其余命名)也应该以一个下划线开头。
若是接口的任一一个命名空间(包,模块或类)是内部的,那么该接口也应该是内部的。
引用的命名应该永远被认为是实现细节。其余模块不该当依赖这些非直接访问的引用命名,除非它们在文档中明确地被写为模块的API,例如os.path
或者包的__init__
模块,那些从子模块展示的功能。
代码应该以不影响其余Python实现(PyPy,Jython,IronPython,Cython,Psyco等)的方式编写。
例如,不要依赖于 CPython 在字符串拼接时的优化实现,像这种语句形式a += b
和a = a + b
。即便是 CPython(仅对某些类型起做用) 这种优化也是脆弱的,不是在全部的实现中都不使用引用计数。在库中性能敏感的部分,用''.join
形式来代替。这会确保在全部不一样的实现中字符串拼接是线性时间的。
与单例做比较,像None
应该用is
或is not
,从不使用==
操做符。
一样的,小心if x is not None
这样的写法,你是不知真的要判断x
不是None
。例如,测试一个默认值为None
的变量或参数是否设置成了其它值,其它值有多是某种特殊类型(如容器),这种特殊类型在逻辑运算时其值会被看成Flase
来看待。
用is not
操做符而不是not ... is
。虽然这两个表达式是功能相同的,前一个是更可读的,是首选。
推荐的写法:
if foo is not None:
不推荐的写法:
if not foo is None:
用富比较实现排序操做的时候,最好实现全部六个比较操做符( __eq__
、 __ne__
、 __lt__
, __le__
, __gt__
, __ge__
),而不是依靠其余代码来进行特定比较。
为了最大限度的减小工做量,functools.total_ordering()
装饰器提供了一个工具去生成缺乏的比较方法。
PEP 207 说明了 Python 假定的全部反射规则。所以,解释器可能使用y > x
替换x < y
,使用y >= x
替换x <= y
,也可能交换x == y
和x != y
的操做数。sort()
和min()
操做确定会使用<
操做符,max()
函数确定会使用>
操做符。固然,最好是六个操做符都实现,以便在其余状况下不会出现混淆。
始终使用def
语句来代替直接绑定了一个lambda
表达式的赋值语句。
推荐的写法:
def f(x): return 2*x
不推荐的写法:
f = lambda x: 2*x
第一个表单意味着生成的函数对象的名称是’f’而不是通用的’<lambda>‘。一般这对异常追踪和字符串表述是更有用的。使用赋值语句消除了使用lambda
表达式能够提供,而一个显式的def
语句不能提供的惟一好处,如,lambda
能镶嵌在一个很长的表达式里。
异常类应派生自Exception
而不是BaseException
。直接继承BaseException
是为Exception
保留的,从BaseException
继承并捕获异常这种作法几乎老是错的。
设计异常的层次结构,应基于那些可能出现异常的代码,而不是引起异常的位置。编码的时候,以回答“出了什么问题?”为目标,而不是仅仅指出“这里出现了问题”(见 PEP 3151 一个内建异常结构层次的例子)。
类的命名约定适用于异常,若是异常类是一个错误,你应该给异常类加一个后缀Error
。用于非本地流程控制或者其余形式的信号的非错误异常不须要一个特殊的后缀。
适当的使用异常链。在 Python 3 里,应该使用raise X from Y
来指示显式替换,而不会丢失原始的追溯。
当有意替换一个内部的异常时(在 Python 2 用raise X
,Python 3.3+ 用raise X from None
),请确保将相关详细信息转移到新异常中(例如,将KeyError
转换为AttributeError
时保留属性名称,或将原始异常的文本嵌入到新的异常消息中)。
在 Python 2 里抛出异常时,用raise ValueError('message')
代替旧式的raise ValueError, 'message'
。
在 Python 3 以后的语法里,旧式的异常抛出方式是非法的。
使用括号形式的异常意味着,当你传给异常的参数过长或者包含字符串格式化时,你就不须要使用续行符了,这要感谢括号!
捕获异常时,尽量使用明确的异常,而不是用一个空的except:
语句。
例如,用:
try: import platform_specific_module except ImportError: platform_specific_module = None
一个空的except:
语句将会捕获到SystemExit
和KeyboardInterrupt
异常,很难区分程序的中断究竟是Ctrl+C
仍是其余问题引发的。若是你想捕获程序的全部错误,使用except Exception:
(空except:
等同于except BaseException
)。
一个好的经验是限制使用空except
语句,除了这两种状况:
raise
向上抛出异常。try .. finally
是处理这种状况更好的方式。绑定异常给一个名字时,最好使用 Python 2.6 里添加的明确的名字绑定语法:
try: process_data() except Exception as exc: raise DataProcessingFailedError(str(exc))
Python 3 只支持这种语法,避免与基于逗号的旧式语法产生二义性。
捕获操做系统错误时,最好使用 Python 3.3 里引进的明确的异常结构层次,而不是内省的errno
值。
另外,对于全部try
/ except
子句,将try
子句限制为必需的绝对最小代码量。一样,这样能够避免屏蔽错误。
推荐的写法:
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
语句确保其在使用后被当即干净的清除了,try/finally
也是也接受的。
当它们作一些除了获取和释放资源以外的事的时候,上下文管理器应该经过单独的函数或方法调用。例如:
推荐的写法:
with conn.begin_transaction(): do_stuff_in_transaction(conn)
不推荐的写法:
with conn: do_stuff_in_transaction(conn)
第二个例子没有提供任何信息来代表__enter__
和__exit__
方法在完成一个事务后作了一些除了关闭链接之外的其它事。在这种状况下明确是很重要的。
坚持使用return
语句。函数内的return
语句都应该返回一个表达式,或者None
。若是一个return
语句返回一个表达式,另外一个没有返回值的应该用return None
清晰的说明,而且在一个函数的结尾应该明确使用一个return
语句(若是有返回值的话)。
推荐的写法:
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)
用字符串方法代替字符串模块。
字符串方法老是快得多,而且与unicode字符串共享相同的API。若是须要与2.0如下的Python的向后兼容,则覆盖此规则。
用''.startswith()
和''.endswith()
代替字符串切片来检查前缀和后缀。
startswith()
和endswith()
是更简洁的,不容易出错的。例如:
#推荐的写法: if foo.startswith('bar'): #不推荐的写法: if foo[:3] == 'bar':
对象类型的比较应该始终使用isinstance()
而不是直接比较。
#推荐的写法: if isinstance(obj, int): #不推荐的写法: if type(obj) is type(1):
当比较一个对象是否是字符串时,记住它有可能也是一个 unicode 字符串!在 Python 2 里面,str
和unicode
有一个公共的基类叫basestring
,所以你能够这样作:
if isinstance(obj, basestring):
注意,在 Python 3 里面,unicode
和basestring
已经不存在了(只有str
),byte
对象再也不是字符串的一种(被一个整数序列替代)。
对于序列(字符串、列表、元组)来讲,空的序列为False
:
正确的写法:
if not seq: if seq:
错误的写法:
if len(seq): if not len(seq):
不要让字符串对尾随的空格有依赖。这样的尾随空格是视觉上没法区分的,一些编辑器(或者,reindent.py)会将其裁剪掉。
不要用==
比较True
和False
。
#推荐的写法: if greeting: #不推荐的写法: if greeting == True: #更加不推荐的写法: if greeting is True:
随着PEP 484被正式接受,函数注释的样式规则已经改变。
为了向前兼容,Python 3代码中的函数注释最好使用PEP 484语法。(上一节中的有一些关于注解格式的建议。)
建议再也不使用在此文档早期版本中描述的试验性质的注解样式。
然而,在标准库(stdlib)以外,如今鼓励在PEP 484的规则范围内的实验。例如,使用PEP 484样式类型的注解标记大型第三方库或应用程序,评估添加这些注解的方式是否简便,并观察其存在是否增长了代码可读性。
Python标准库在采用这些注解时应持谨慎态度,可是当编写新代码或进行大的重构时,容许使用。
若是但愿不按照函数注解的方式来使用函数,能够在文件头部添加如下注释:
# type: ignore
这会告诉类型检查器忽略全部注解。 (在PEP 484中能够找到更细致的方式来控制类型检查器的行为。)
像代码扫描工具同样(linters),类型检查器是可选的,单独的工具。默认状况下,Python解释器不该该因为类型检查而发出任何消息,而且不该该根据注释来改变它们的行为。
不想使用类型检查器的用户能够忽略它们。可是,预计第三方库软件包的用户可能但愿在这些软件包上运行类型检查器。为此,PEP 484 建议使用存根文件:.pyi文件,类型检查器优先于相应的.py文件读取这个文件。存根文件能够与库一块儿分发,也能够单独地(在库做者许可的状况下)经过Typeshed repo7分发。
对于须要向后兼容的代码,能够以注释的形式添加类型注解。参见PEP 484的相关章节。
此文档没有版权限制。
来源: https://github.com/python/peps/blob/master/pep-0008.txt
Python之父 ↩
Barry的GNU Mailman编码风格指南 http://barry.warsaw.us/software/STYLEGUIDE.txt ↩
出自Ralph Waldo Emerson。 ↩
挂起缩进是一种类型设置样式,除了第一行外,段落中的全部行都缩进。在Python的上下文中,该术语用于描述括号括起来的语句的左括号是该行的最后一个非空格字符的样式,后面的行将缩进直到右括号。 ↩
Donald Knuth的《The TeXBook》, 第195和196页。 ↩
即用"""
而不是'''
↩
Typeshed repo https://github.com/python/typeshed ↩