代码布局
缩进
推荐的两种方式:
- 垂直对齐:与(), [], {}对齐
- 悬挂缩进
建议
# 垂直对齐,与外面(), [], {}对齐
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)
不建议
# 如果使用悬挂缩进而不是垂直对齐,第一行就不要有参数。
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)
括号结束的处理
- 可以在最后一个元素后面
- 可以和语句开头第一个字符对齐
- 可以和上一行第一个不为空格的字符对齐
# 和上一行的第一个不为空格的字符对齐
result = some_function_that_takes_arguments(
'a', 'b', 'c',
'd', 'e', 'f',
)
# 和语句开头的第一个字符对齐
my_list = [
1, 2, 3,
4, 5, 6,
]
if语句的缩进
# 无额外的缩进.
if (this_is_one_thing and
that_is_another_thing):
do_something()
# 添加一条注释来分隔if语句的两个部分
# supporting syntax highlighting.
if (this_is_one_thing and
that_is_another_thing):
# Since both conditions are true, we can frobnicate.
do_something()
# 添加额外的缩进来分隔if语句的两个部分
if (this_is_one_thing
and that_is_another_thing):
do_something()
Tab
还是空格
- 推荐使用空格
- 只有在兼容的时候使用
Tab
-
Python 3
不允许两者混合使用。 -
Python 2
用选项-t
可以在混合使用的时候发出警告;使用选项-tt
会发出错误。
单行的最大长度
79个字符。
对于docstring
和注释:72个字符。
好处:
- 在屏幕两边同时打开不同文件
- 代码评审时,在屏幕两侧对比同一文件的不同版本
实践:
- 一致就好
- 能不使用反斜杠就不使用:用括号将内容括起来。
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())
换行到底在二元操作符前面还是后面?
# No: operators sit far away from their operands
income = (gross_wages +
taxable_interest +
(dividends - qualified_dividends) -
ira_deduction -
student_loan_interest)
# Yes: easy to match operators with operands
income = (gross_wages
+ taxable_interest
+ (dividends - qualified_dividends)
- ira_deduction
- student_loan_interest)
空行
- 顶层的函数、类用两个空行分隔
- 类内部的方法定义用一个空行分隔
- 在函数内部使用空行表示逻辑的分块
- 可以使用
control-L
作为换页符,貌似pycharm并不支持
文件编码
UTF-8
import
语句
- 分开使用
# Yes:
import os
import sys
# No:
import sys, os
# OK:
from subprocess import Popen, PIPE
- 在文件最上方:模块注释和
docstring
之后,globals
和常量之前。顺寻应该是(每组之间用空行隔开):- 标准库
- 相关的第三方库
- 本地库
- 导入一个类时:
from myclass import MyClass
from foo.bar.yourclass import YourClass
# 如果类名有冲突
import myclass
import foo.bar.yourclass
# 使用时:"myclass.MyClass" "foo.bar.yourclass.YourClass".
- 尽量避免
import mypkg.*
。
模块级的dunder name
模块级的dunder name,比如__all__, __version__, __author__
应该放在import
之前,但是在from __future__ 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
字符串的引号
单引号双引号都可以,那么选择一个,然后保持一致。
表达式和语句中的空格
一定不要这么做!
- 括号内,紧挨括号的地方,不要加空格
# Yes:
spam(ham[1], {eggs: 2})
foo = (0,)
# No:
spam( ham[ 1 ], { eggs: 2 } )
bar = (0, )
-
, ; :
之前,不要加空格
# Yes:
if x == 4: print x, y; x, y = y, x
# No:
if x == 4 : print x , y ; x , y = y , x
- 在切片中使用
:
的时候,把它当作一个操作符来考虑:两边使用同样多的空格。例外:如果切片中有的参数省略了,那么该参数的空格也要省略
# Yes:
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]
# No:
ham[lower + offset:upper + offset]
ham[1: 9], ham[1 :9], ham[1:9 :3]
ham[lower : : upper]
ham[ : upper]
- 参数列表的小括号
(
之前,不要加空格
# Yes:
spam(1)
foo = (0, 1)
# No:
spam (1)
- 索引或切片操作的中括号
[
之前,不要加空格
# Yes:
dct['key'] = lst[index]
# No:
dct ['key'] = lst [index]
- 赋值操作符前后只要一个空格,不要用过多的空格来对齐
# Yes:
x = 1
y = 2
long_variable = 3
# No:
x = 1
y = 2
long_variable = 3
其他的建议
- 行尾不要有空格
- 为下列二维操作符的前后各加一个空格:赋值、比较、
booleans
。 - 在同一句语句中,可以省略优先级较高的操作符之间的空格。二元操作符前后的数量一定要相等,一定不要超过一个。
- 关键字参数和默认参数中的
=
前后不要加空格
# Yes:
def complex(real, imag=0.0):
return magic(r=real, i=imag)
# No:
def complex(real, imag = 0.0):
return magic(r = real, i = imag)
- 函数和参数的注解:冒号
:
前无空格,后跟空格;->
前后各一个空格;既有注解又有默认值时,等号=
前后各一个空格。
# Yes:
def munge(sep: AnyStr = None): ...
def munge() -> AnyStr: ...
def munge(input: AnyStr, sep: AnyStr = None, limit=1000): ...
# No:
def munge(input: AnyStr=None): ...
def munge()->PosInt: ...
def munge(input: AnyStr, limit = 1000): ...
什么时候使用结尾的逗号
- 只有一个元素的tuple。这种情况最好使用括号。
# Yes:
FILES = ('setup.cfg',)
# OK
FILES = 'setup.cfg',
- 在一个值占一行,且右括号
)]}
单独占一行的情况,可以加一个多于的逗号在句尾。可以方便diff
。
# Yes:
FILES = [
'setup.cfg',
'tox.ini',
]
initialize(FILES,
error=True,
)
# No:
FILES = ['setup.cfg', 'tox.ini',]
initialize(FILES, error=True,)
注释
- 不要与代码矛盾!改代码也要改注释
- 块注释必须由完整的句子组成,用句号分隔
- 最好用英文
行内注释
- 不要太多
- 与语句至少2个空格,
#
后跟一个空格。
文档字符串 docstrings
- 为公共的模块、函数、类、方法写注释。
- 格式
"""Return a foobang
Optional plotz says to frobnicate the bizbaz first.
"""
命名规范
让所有代码都遵守一致的命名规范很难的,可以要求新的模块和包遵守一定的规范,但已有的库可能不满足,这时候需要在内部保持一致。
首要原则
对外的接口,要反映usage,而不是implementation
常见的命名方式
- b
- B
- lowercase
- lower_case_with_underscores
- UPPERCASE
- UPPER_CASE_WITH_UNDERSCORES
- CapitalizedWords
- mixedCase
- Capitalized_Words_With_Underscores
Python中的一些约定 - _single_leading_underscore
- single_trailing_underscore_
- __double_leading_underscore
- __double_leading_and_trailing_underscore__
规范
- 模块名:小写字母+下划线
- 包名:小写字母。
e.g. timeit
- 类名:首字母大写。
e.g. CapWords
- 异常名:首字母大写+后缀
Error
- 函数名、变量名、方法名、实例变量名:小写+下划线
- 参数名:小写+下划线;
self
和cls
是约定;与关键字冲突的时候,在后面加_
- 常量:大写字母+下划线
继承的设计
- 公共属性不要以下划线开头。
- 如果属性与关键字冲突,在结尾地方加一个下划线。
- 对public的数据属性,最好不要getter/setter(accessor/mutator)方法。
- 如果有属性不想让子类使用,则命名为
__foo
,触发python的命名转换。
公共接口和内部接口
- 必须保证公共接口向后兼容,对内部接口则不用
- 必须为公共借口提供文档
- 使用all声明公共接口
- 内部接口(包、模块、类、函数等)使用前缀
_
编程建议
- 不要依赖与具体的python实现(PyPy, Jython, IronPython, Cython)
- 与单例对象(比如
None
)作比较的时候,用is
和is not
,而不是==
。 - 使用
is not
而不是not ... is
。
# Yes:
if foo is not None:
...
# No:
if not foo is None:
...
- 使用
def
,而不是赋值+lambda
。 -
python2
中,用raise ValueError('message')
代替raise ValueError, 'message'
。后者在python3
是错误的。 - 为
expect
指明异常类型,需要异常名时,使用as
。
try:
foo()
expect Exception as exc:
print(exc)
-
try
语句块内的语句尽可能的少。 - 局部使用的资源,使用
with
。
自动格式化的工具
black
- 基于
python3
,可以格式化python2
的代码 - 比较专制,遵守
pep8/pycodestyle
- 默认全部使用双引号(为啥?),可以用选项--skip-string-normalization来跳过,不更改字符串的引号。
- 在列表最后项的末尾加了多于的逗号
,
- 去除不必要的反斜杠
\
- 过长的字符串不做处理
- 列表太多太长时:一项独占一行
- 用
# fmt: off
和# fmt: on
来标记不需要format的语句块,兼容yapf
的标记。 - make
pycodestyle
happy.PEP8
的子集 - 在二元操作符前换行
- 切片:
PEP8
如何处理折行?
1. 尝试不折行
2. 打开最外层括号,括号内内容单独一行
3. 还是不行,迭代打开括号
4. 如果内容没有括号,有逗号做分隔,尝试同一行
5. 放不下->每一项独占一行,并加一个多于的逗号。(便于diff)
# in:
def very_important_function(template: str, *variables, file: os.PathLike, debug: bool = False):
"""Applies `variables` to the `template` and writes to `file`."""
with open(file, 'w') as f:
...
# out:
def very_important_function(
template: str,
*variables,
file: os.PathLike,
debug: bool = False,
):
"""Applies `variables` to the `template` and writes to `file`."""
with open(file, "w") as f:
...
行的长度
默认88,为了让文件长度更短。
尺有所短寸有所长。
空行
遵循PEP8
。
- 在函数前后插入一个空行,在模块级的类和函数的前后插入两个空行。
- 类的
docstrings
后紧跟一个空行 - 函数的
docstrings
后一般不跟空行,除非后面紧跟一个内部函数。
tailing commas
- 在独占一行的时候添加
isort
自动给import
语句排序。
静态代码分析工具
pycodestyle
原名pep8。参照PEP 8。定义了Error codes。
Pylint
flake8
formatter
yapf
black
- “骄傲”
autopep8
- 使用
pycodestyle
来决定需要格式化的代码,修复它报的大部分格式问题。 - 默认,只做空格空行的增减,不会修正
x == None
。加选项--agressive
。 -
--select
修正指定的错误/警告。 - 可以作为模块使用。
- 以
PEP 8
和pycodestyle
为导向