Python编码规范(Style Guide for Python Code)
参考原文《Style Guide for Python Code》。
Guido:“code is read much more often than it is written”
缩进
每个缩进级别使用四个空格。
函数
正例:
函数调用时多个参数分多行书写,
第一种方式,参数需要对齐:
foo = long_function_name(var_one, var_two,
var_three, var_four)
第二种方式,每行参数缩进四个空格,如下:
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)
第二种方式:
More indentation included to distinguish this from the rest.
def long_function_name(var_one, var_two, var_three,
var_four):
print(var_one)
反例:
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)
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()
不限于以上三种方式。
括号
第一种方式,数据换行缩进4个空格,结尾括号与数据对齐,如下:
my_list = [
1, 2, 3,
4, 5, 6,
]
result = some_function_that_takes_arguments(
'a', 'b', 'c',
'd', 'e', 'f',
)
也可以数据换行缩进4个空格,结束括号与开始行首对齐,如下:
my_list = [
1, 2, 3,
4, 5, 6,
]
result = some_function_that_takes_arguments(
'a', 'b', 'c',
'd', 'e', 'f',
)
Tab键还是Spaces?
(1)空格是首选缩进的方法。
(2)Python 3不允许在缩进中混合使用制表符和空格
(3)使用制表符和空格混合缩进的Python 2代码应该转换为只使用空格
(4)制表符应该只用于与已经缩进的代码保持一致
最大行宽
每行限制为最大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())
在二元操作符之前还是之后断开?
两者都可以,推荐:
income = (gross_wages
+ taxable_interest
+ (dividends - qualified_dividends)
- ira_deduction
- student_loan_interest)
空白行:
(1)用两行空白行包围顶级函数和类定义
(2)类中的方法定义由单个空行包围
(3)在函数中尽量使用空白行来表示逻辑部分
(4)可以(少量地)使用额外的空行来分隔相关的函数组
源文件编码:
(1)核心Python发行版中的代码应该始终使用UTF-8(或Python 2中的ASCII)
Imports
导入通常应该在不同的行上,例如:
import os
import sys
from subprocess import Popen, PIPE
不推荐如下方式:
import sys, os
imports总是放在文件的顶部,在模块注释和文档字符串之后,在模块全局变量和常量之前。如下:
“”"
This module gathers tree-based methods, including decision, regression and
randomized trees. Single and multi-output problems are both handled.
“”"
Authors: Gilles Louppe g.louppe@gmail.com
Peter Prettenhofer peter.prettenhofer@gmail.com
Brian Holt bdholt1@gmail.com
Noel Dawe noel@dawe.me
Satrajit Gosh satrajit.ghosh@gmail.com
Joly Arnaud arnaud.v.joly@gmail.com
Fares Hedayati fares.hedayati@gmail.com
Nelson Liu nelson@nelsonliu.me
License: BSD 3 clause
from __future__ import division
import numbers
import warnings
from abc import ABCMeta
from abc import abstractmethod
from math import ceil
import numpy as np
from scipy.sparse import issparse
from ..base import BaseEstimator
from ..base import ClassifierMixin
from ..base import RegressorMixin
from ..base import is_classifier
from ..externals import six
from ..utils import check_array
from ..utils import check_random_state
from ..utils import compute_sample_weight
from ..utils.multiclass import check_classification_targets
from ..utils.validation import check_is_fitted
from ._criterion import Criterion
from ._splitter import Splitter
from ._tree import DepthFirstTreeBuilder
from ._tree import BestFirstTreeBuilder
from ._tree import Tree
from . import _tree, _splitter, _criterion
__all__ = ["DecisionTreeClassifier",
"DecisionTreeRegressor",
"ExtraTreeClassifier",
"ExtraTreeRegressor"]
=============================================================================
Types and constants
=============================================================================
DTYPE = _tree.DTYPE
DOUBLE = _tree.DOUBLE
CRITERIA_CLF = {"gini": _criterion.Gini, "entropy": _criterion.Entropy}
CRITERIA_REG = {"mse": _criterion.MSE, "friedman_mse": _criterion.FriedmanMSE,
"mae": _criterion.MAE}
DENSE_SPLITTERS = {"best": _splitter.BestSplitter,
"random": _splitter.RandomSplitter}
SPARSE_SPLITTERS = {"best": _splitter.BestSparseSplitter,
"random": _splitter.RandomSparseSplitter}
导入顺序:
Standard library imports.
Related third party imports.
Local application/library specific imports.
每组之前使用空白行。
建议使用绝对导入:
import mypkg.sibling
from mypkg import sibling
from mypkg.sibling import example
尽量避免通配符导入(from import *) 。
模块级“Dunders”名称
“Dunders”名称:带有两个前导下划线和两个后置下划线的名称,例如: all, author, __version__等等。应当放在模块文档(module docstring)之后,所有import语句之前(from future imports除外,from future imports放在模块文档之后,所有import语句之前),例如:
“”“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
字符串引用(String Quotes)
在Python中,单引号和双引号是一样的。本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
不用为了赋值符号对齐而使用多个空格,如下:
推荐:
x = 1
y = 2
long_variable = 3
不推荐:
x = 1
y = 2
long_variable = 3
推荐:
spam(1)
dct['key'] = lst[index]
不推荐:
spam (1)
dct ['key'] = lst [index]
推荐:
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]
推荐:
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)
注释(Comments)
与代码冲突的注释比没有注释更糟糕。当代码发生变化时,始终优先保持注释的更新。注释应该是完整的句子,第一个单词应该大写,除非它是一个以小写字母开头的标识符(永远不要改变标识符的大小写!)。来自非英语国家的Python程序员:请用英语写您的注释,除非您有120%的信心这些代码永远不会被不使用您的语言的人阅读。
块注释(Block Comments)
块注释一般使用在几行代码之前,与代码缩进相同,每行使用#开始,注释内容和#之间有一个空格,例如:
The SAG solver releases the GIL so it’s more efficient to use
threads for this solver.
if self.solver in ['sag', 'saga']:
backend = 'threading'
else:
backend = 'multiprocessing'
如果注释分多个段落,段落之间使用单个#分隔,例如:
The SAG solver releases the GIL so it’s more efficient to use
threads for this solver.
if self.solver in ['sag', 'saga']:
backend = 'threading'
else:
backend = 'multiprocessing'
行内注释(Inline Comments)
内联注释是与语句在同一行上的注释。尽量少用行内注释。语句与行内注释之间至少两个空格或更多,#和注释内容之间一个空格,如下:
x = x + 1 # Compensate for border
文档字符串(Documentation Strings)
Documentation Strings也称为docstrings。为所有公共模块、函数、类和方法编写文档字符串。对于非公共方法,docstring不是必需的,但是应该有一个描述该方法功能的注释。这个注释应该出现在def行之后。描述良好的文档字符串约定:结束多行docstring的三个双引号"""应该单独在一行上,例如:
“”“Return a foobang
Optional plotz says to frobnicate the bizbaz first.
“””
对于单行docstring,三个双引号"""应该在与文档字符串在同一行上,例如:
def kos_root():
"""Return the pathname of the KOS root directory."""
global _kos_root
if _kos_root: return _kos_root
详细参见《Docstring Conventions》。
命名约定(Naming Conventions)
Python库的命名约定有点混乱,所以我们永远不会得到完全一致的结果——尽管如此,下面是目前推荐的命名标准。应该按照这些标准编写新的模块和包(包括第三方框架),但是如果现有库具有不同的风格,则最好采用内部一致性。
几种命名风格:
单个小写字符:b
单个大写字符:B
小写单词:age
大写单词:NAME
下划线连接小写单词:lower_case_with_underscores
下划线连接大写单词:UPPER_CASE_WITH_UNDERSCORES
驼峰命名(每个单词首字母大写):CapitalizedWords
每个单词首字母大写(第一个单词首字母小写):mixedCase
另外,使用前导或后置下划线的特殊格式:
(1)_single_leading_underscore:前面添加单个下划线表示内部使用,例如:import语句from M import *不会导入单个下划线开始的对象。
(2)single_trailing_underscore_:后面添加单个下划线避免与关键字冲突,例如:
Tkinter.Toplevel(master, class_='ClassName')
(3)__double_leading_underscore:命名类属性时使用双下划线开头。
(4)double_leading_and_trailing_underscore:前后使用双下划线,用于命名空间中的魔法(“magic”)对象和属性。例如:init, import or file,永远不要发明这样的名字;只在文档中使用它们。
不要使用“l”,“O”,“i”等作为单个字符变量的名字,因为有时它们无法与数字“1”和“0”区分。
包和模块名
模块名字全部小写,可以使用下划线连接。
Python包也全部使用小写字母,不推荐使用下划线连接。
When an extension module written in C or C++ has an accompanying Python module that provides a higher level (e.g. more object oriented) interface, the C/C++ module has a leading underscore (e.g. _socket).
类名称(Class Names)
类名使用驼峰命名法。
类型变量名称(Type Variable Names)
PEP 484中引入的类型变量的名称通常应该使用驼峰命名法,它们更喜欢短名称:T、AnyStr、Num。建议在用于声明协变或逆变行为的变量中添加后缀_co或_contra,例如:
from typing import TypeVar
VT_co = TypeVar('VT_co', covariant=True)
KT_contra = TypeVar('KT_contra', contravariant=True)
异常名称(Exception Names)
异常也是一个类,使用驼峰命名法,但是应当添加“Error”后缀。
全局变量名称(Global Variable Names)
我们希望这些变量只在一个模块中使用。
函数和变量名称(Function and Variable Names)
函数名和变量名使用小写字母,单词之间使用下划线。变量命名和函数相同。mixedCase只允许在已经是主流样式的上下文中使用(例如thread .py),以保持向后兼容性。
函数和方法参数(Function and Method Arguments)
(1)实例方法:始终使用self作为第一个参数;
(2)类方法:始终使用cls作为第一个参数;
如果函数参数的名称与保留关键字冲突,通常最好附加一个尾随下划线,而不是使用缩写或拼写错误。因此class_比clss好。(或许更好的办法是使用同义词来避免这种冲突。)
方法名称和实例变量(Method Names and Instance Variables)
(1)和函数名称命名规定相同
(2)仅对非公共方法和实例变量使用一个前导下划线
常量(Constants)
常量通常在模块级别上定义,并使用大写字母和下划线分隔单词。例如MAX_OVERFLOW和TOTAL。
继承的设计(Designing for Inheritance)
总是决定类的方法和实例变量(统称:“属性”)是公共的还是非公共的。如有疑问,选择非公开;稍后将其公开比将公共属性设置为非公共更容易。我们在这里不使用术语“私有”,因为在Python中没有真正私有的属性。
(1)公共属性应该没有前导下划线
(2)如果公共属性名与保留关键字冲突,请在属性名后面追加一个下划线
(3)如果类打算被子类化,并且不希望子类使用的属性,那么可以考虑使用双前导下划线和无后置下划线来命名它们。
公共接口和内部接口(Public and Internal Interfaces)
(1)为了更好地支持自省,模块应该使用__all__属性显式地在其公共API中声明名称。将__all__设置为空列表表明模块没有公共API。
设计推荐(Programming Recommendations)
(1)代码的编写方式应该不影响Python的其他实现(PyPy、Jython、IronPython、Cython、Psyco等)。例如,不依赖CPython的高效实现就地字符串连接的语句形式+ = b或a = a + b。这种优化是脆弱的甚至在CPython的(只适用于某些类型)和不存在在不使用refcounting实现。在库的性能敏感部分,应该使用“.join()表单”。这将确保在不同实现之间以线性时间进行连接。
(2)使用 is not 而不是not…is。虽然这两个表达式在功能上是相同的,但前者更易于阅读和首选。
(3)与None之类的单例进行比较时,应该始终使用is或is not而不是相等运算符。
(4)在实现具有丰富比较的排序操作时,最好实现所有6个操作(eq、ne、lt、le、gt、ge),而不是依赖其他代码只执行特定的比较。
(5)始终使用def语句,而不是直接将lambda表达式绑定到标识符的赋值语句。
推荐:
def f(x): return 2*x
不推荐:
f = lambda x: 2*x
(6)捕获异常时,尽可能地提到特定的异常,而不是使用空的except:子句
try:
import platform_specific_module
except ImportError:
platform_specific_module = None
(7)不要使用==将布尔值与True或False进行比较。
Yes: if greeting:
No: if greeting == True:
Worse: if greeting is True:
(8)对于序列(字符串、列表、元组),使用空序列为假的事实
Yes: if not seq:
if seq:
No: if len(seq):
if not len(seq):
(9)对象类型比较应该始终使用isinstance(),而不是直接比较类型
Yes: if isinstance(obj, int):
No: if type(obj) is type(1):