Python Enhancement Proposal #8笔记
官网:PEP 8: The Style Guide for Python Code
参考:(90条消息) Python PEP8 编码规范中文版_冒冒大虾的博客-CSDN博客
1、代码布局
1.1 缩进
a、每级缩进使用4个空格。
b、不混用空格和制表符。
如对函数示例
foo = get(var_a,var_b,var_c,var_d)
通常采用垂直缩进或悬挂缩进,如下:
垂直缩进:首行带参数,下一行参数对其左括号
悬挂缩进:首行不带参数,下一行参数多一级缩进
# 垂直缩进,对齐括号
foo = get(var_a,var_b,
var_c,var_d)
# 悬挂缩进,不对齐括号,多加一级缩进,这里是两级,8个空格
foo = get(
var_a,var_b,
var_c,var_d)
注:对于悬挂缩进,可不严格采用缩进4个空格,如下:
foo = get(
var_a,var_b,
var_c,var_d)
1.2 跨行的if语句
当if语句具有and、or情况时,同样有垂直缩进、悬挂缩进两种形式:
if垂直缩进:and放置首行,第二个条件对齐左括号
if(one_thing and
two_thing)
do()
if悬挂缩进:and放置第二行,加一级缩进
if(one_thing
and two_thing)
do()
1.3 右括号,对所有括号
a、右括号换行,不回退
mat = [
1,2,3
4,5,6
]
result = do_some(
val1,val2
val3,val4
)
b、右括号换行,回退
mat = [
1,2,3
4,5,6
]
result = do_some(
val1,val2
val3,val4
)
1.4 空格和tab规范
空格是首选的缩进方法。
Tab仅仅在已经使用tab缩进的代码中为了保持一致性而使用。
Python 3中不允许混合使用Tab和空格缩进。
Python 2的包含空格与Tab和空格缩进的应该全部转为空格缩进。
Python2命令行解释器使用-t选项时有非法混合Tab和空格的情况会告警。当使用-tt警告提升为错误。
2、最大行宽
在coding时,注意限制代码、注释最多72个字符。
因为多数工具默认的续行功能会破坏代码结构,使它更难理解,不推荐使用。
2.1 续行
在各种括号中,可以直接按照缩进规范换行,此外,在代码末尾添加反斜线‘\’实现换行。
如:
with open(path1) as f1, \
open(path2) as f2:
do(f1,f2)
2.2 空行
a、函数和类之间的空行:2行分割
b、类的方法定义之间空行:单行分割。
c、必要时,可用额外空行分割不同的函数组----尽量节约使用。
d、必要时,可用额外空行分割不同的逻辑块-----尽量节约使用。
e、Python接 contol-L作为空白符;许多工具视它为分页符,这些要因编辑器而异。
2.3 源文件编码
核心Python发布代码:应该总是使用UTF-8(ASCII在Python 2)。
ASCII文件(Python 2)或UTF-8(Python 3)不应有编码声明。
标准库中非默认的编码应仅用于测试或当注释或文档字符串,比如包含非ASCII字符的作者姓名,尽量使用\x , \u , \U , or \N。
Python 3.0及以后版本,PEP 3131可供参考,部分内容如下:在Python标准库必须使用ASCII标识符,并尽量只使用英文字母。此外字符串和注释也必须用ASCII。唯一的例外是:
(a)测试非ASCII的功能,和(b)作者的名字不是拉丁字母。
3、导入包的规范
导入包的顺序应该符合下列组顺序,且每个组之间空一行:
a、标准库:os、time等
b、第三方库:pandas、matplotlib、torch 等
c、自定义.py文件/函数
导入的库应该处于单独行。
如:
# 先导入标准库,两个标准库处于单独行
import math
import os
# 再导入需要的第三方库
import matplotlib.pyplot as plt
# 最后导入自定义库
import MyPyFile as my_py
此外,应注意导入应处于文件的顶部,且使用绝对路径导入。
不推荐使用:通配符导入,如:from A import *,这里会导入A的所有工具,可能会与自定义或其它库存在命名冲突。
4、字符串引用
在python中,使用单引号、双引号均可,但需要尽量避免在字符串中的反斜杠,以提高可读性。
5、表达式、语句中的空格
5.1 强制要求
a、括号中尽量避免空格,如圆括号首参数前;
spam(ham[1], {eggs: 2})
b、逗号、冒号、分号之前避免空格;
if x == 4: print x, y:x, y = y, x
c、索引中冒号前后采用一样的空格数,或无空格
x = a[:-2]
y = b[-5:][get_loc(x)]
d、函数调用、索引等左括号前不能有空格;
func(a)
x['key'] = value1
e、操作符前后空格数量应一致,推荐前后1空格;
x = 3
b = x == 3
5.2 建议
a、二元运算符前后加空格,优先级高的运算符前后则不加空格
涉及 =、符合操作符 ( += , -=等)、比较( == , < , > , != , <> , <= , >= , in , not in , is , is not )、布尔( and , or , not )。
x = a + 1
y = (x*3) + 3
b、关键字参数、默认参数前后不加空格
def do_some(val1, val2=0.5): # 默认参数
return val1 + val2
def do_complex(a, b=20):
return do_some(val1=a, val2=b) # 关键字参数
c、函数注释中,=前后要有空格,冒号和"->"的前面无空格,后面有空格。
def munge(input: AnyStr):
def munge(sep: AnyStr = None):
def munge() -> AnyStr:
def munge(input: AnyStr, sep: AnyStr = None, limit=1000):
d、避免多行语句写在一行
Yes
if x == 3:
print(x)
No
if x == 3: print(x)
6、注释
尽量用英语写注释,修改代码时,优先更新注释。
6.1 注释位置
a、代码上方,与代码缩进位置相同;
b、代码右侧,采用两个空格隔开。
# get result
result = do()
a = b + 1 # get the value of a
6.2 文档字符串
即三个双引号
"""return a
there is a words line
"""
通常在函数定义def下方增加文档字符串,用于说明该函数的作用,注意,结尾的"""在单独一行。
7、版本标签
源文件中git、subversion、cvs、rcs crud信息,放在文档字符串之后,任何其它代码之前,上下各用一个空行。
__version__ = "$Revision$"# $Source$
8、命名规定
8.1 命名规避字母
避免使用小写的字母‘eoh’l,大写字母‘ai’I,大写字母‘oh’O,作为单个变量名或变量名的首字母。
8.2 命名约定规范
类命名:首字母大写的驼峰命名法,如:class MyCls
模块命名:小写字母命名,每个单词之间用下划线隔开,增加可读性,如:module_temp
包命名:小写字母命名,但不用下划线,如:firstpackage
注:模块是一个单独的文件,包含了 Python 的定义、语句和可执行代码(即包含类,函数等)。包是一个包含模块和其他包的文件夹,用于组织和管理相关的模块。
异常名:在类名后缀‘Error’
函数名:小写字母命名,单词之间用下划线隔开,如:def my_func():,注,如c++一般的首字母小写的驼峰命名myFunc仅允许用于兼容性考虑。
全局变量名、方法名、实例变量:小写字母命名单词之间用下划线隔开。
函数和方法的参数:实例第一个参数为self,类方法第一个参数为cls。当函数参数与关键字冲突,在参数名后加一个下划线,如:param_。
常量:大写字母加下划线,如:MAX_ART_LIST
继承设计:
考虑类的方法和实例变量(统称为属性)是否公开。如果有疑问,选择不公开;把其改为公开比把公开属性改为非公开要容易。
公开属性可供所有人使用,并通常向后兼容。非公开属性不给第三方使用、可变甚至被移除。
这里不使用术语"private", Python中没有属性是真正私有的。
另一类属性是子类API(在其他语言中通常称为 “protected”)。 一些类被设计为基类,可以扩展和修改。
谨记这些Python指南:
a、公开属性应该没有前导下划线。
b、如果公开属性名和保留关键字冲突,可以添加后置下划线
c、简单的公开数据属性,最好只公开属性名,没有复杂的访问/修改方法,python的 Property提供了很好的封装方法。d.如果不希望子类使用的属性,考虑用两个前置下划线(没有后置下划线)命名。
8.3 公共和内部接口
任何向后兼容的保证只适用于公共接口。
文档化的接口通常是公共的,除非明说明是临时的或为内部接口、其他所有接口默认是内部的。(即,内部接口不开放,只开放一个公共接口)
为了更好地支持内省,模块要在__all__属性列出公共API。
内部接口要有前置下划线。
如果命名空间(包、模块或类)是内部的,里面的接口也是内部的。
导入名称应视为实现细节。其他模块不能间接访名字,除非在模块的API文档中明确记载,如os.path中或包的__init__暴露了子模块。
9、编程建议
使用多种python实现,PyPy、Jython、IronPython、Pyrex、Psyco等。
9.1 使用.join(),而不是+=
CPython中 +=、-= 较快,而在Jython很慢,应该使用.join()。
9.2 is与=
在None的比较中,使用is 或 is not, 而不用=。
Yes
if a is None:
do()
No
if a == None:
do()
9.3 用is not ,而不用not。。is
Yes
if a is not None:
No
if not a is None:
9.4 使用基于类的异常
比较排序操作最好是实现所有六个操作,而不是代码中实现比较逻辑。functools.total_ordering()装饰符可以生成缺失的比较方法。
9.5 避免使用lambda匿名函数赋值给标识符
因为lambda不适用于回调和字符串表示。
# Yes
def f(x):
return 2*x
# No
f = lambda x: 2*x
9.6 将异常类继承自Exception,而不是BaseException
源于异常,而不是BaseException例外。从BaseException直接继承的例外情况追赶他们几乎总是错误的事情做保留。
要设计基于层次的异常,捕捉到需要的异常,而不是异常引发的位置。能回答:“出了什么问题?”,而不是仅仅指出“问题发生”(参考:https://www.python.org/dev/peps/pep-3151)
9.7 适当使用异常链
异常链:指在捕获异常的同时,保留原始异常的信息并将其包装在新的异常中,以便更好地追踪和调试问题。
作用:提高代码的可读性和可维护性。
在Python3中"raise X from Y"明确表示更换且保留了原来的traceback。
替换内部异常(在Python2: “raise X"或"raise X from None”)时,确保相关细节转移到新的异常(如转换KeyError为AttributeError保存属性名,或在新的异常中嵌入原始异常)。
Python2中用" raise ValueError(‘message’)“代替"raise ValueError, ‘message’”
后者不兼容Python3语法。前者续行方便。
捕获异常时尽量指明具体异常,而不是空"except:"子句。如:
# Yes
try:
import platform_specific_module
except ImportError:
platform_specific_module = None
空"except:“子句(相当于except Exception)会捕捉SystemExit和KeyboardInterrupt异常,难以用Control-C中断程序,并可掩盖其他问题。如果 你捕捉信号错误之外所有的异常,使用"except Exception”。
空"except:"子句适用的情况两种情况:
a, 打印出或记录了traceback,至少让用户将知道已发生错误。
b, 代码需要做一些清理工作,并用 raise转发了异常。这样try…finally可以捕捉到它。
绑定异常名:python2.6之后支持
try:
do()
except Exception as exc:
raise DataProcessingFailedError(str(exc))
操作系统错误时,建议使用Python 3.3引入显式异常层次,支持内省errno值。
所有try/except子句的代码要尽可的少,以免屏蔽其他的错误
# Yes
try:
value = collection[key]
except KeyError:
return key_not_found(key)
else:
return handle_value(value)
# No
try:
# 太泛了!
return handle_value(collection[key])
except KeyError:
# 会捕捉到handle_value()中的KeyError
return key_not_found(key)
9.8 本地资源使用with语句
对本地资源使用with语句可以确保及时清理,但try/finally也是可用的。
9.9 上下文管理器在做获取和释放资源之外的事情时,应通过独立的函数或方法。
# Yes
with conn.begin_transaction():
do_stuff_in_transaction(conn)
# No
with conn:
do_stuff_in_transaction(conn)
9.10 函数或方法在没有返回时要明确返回None
Yes
def func(a):
if a > 10:
return a
else:
return None
No
def func(a):
if a > 10:
return a
9.11 检查字符串前缀和后缀时,使用.startswith()和.endswith(),而不使用字符串切片
str = 'bar is bar'
Yes
if str.startswith('bar'):
No
if str[:3] == 'bar':
9.12 用isinstance()进行对象类型比较
Yes
if isinstance(obj,int):
No
if type(obj) is int:
此外,对于对于序列对象(字符串,列表,元组),空序列为false
seq = []
if seq: # 这里为False
pass
if not seq:
pass
9.13 布尔比较
使用is ,不用 ==
if bool1 is True:
do()