Python的作者既优雅又高冷又龟毛的在 PEP 8 里规定了 Python 程序编写时应该遵循的风格与格式。我通读了一遍,把我目前可以理解的总结在这里以备日后参考。
一. 基本观念
- 可读性至上。代码被读的次数肯定远比被写的次数多。因此作者十分重视代码的可读性,后文里的很多规定也都是基于这个出发点来考虑的。
- 一致性次之。如果原有代码的style与本指南不符,优先考虑保持一致性。但是新编写的模块优先遵循风格指南。
二. 具体操作
-
布局与格式
- 缩进:优先用4个空格而不是制表符(tab)
- 多行连续语句:对齐。几个正面例子:
# 括号内开头对齐 foo = long_function_name(var_one, var_two, var_three, var_four) # Hanging indent: 括号第一行不填,第二行开始括号内的部分加倍缩进(多4个空格) def long_function_name( var_one, var_two, var_three, var_four): print(var_one)
- 多行的构建赋值:后半括号要独立一行,且和前面的行对齐,两种方式皆可。
my_list = [ 1, 2, 3, 4, 5, 6, ] my_list = [ 1, 2, 3, 4, 5, 6, ]
- 行的长度少于72个字符:主要是为了便于分屏比对代码(作者操心命)。换行的操作是:优先用括号把并列的长语句括起来,然后就可以在连接处出换行了;实在不行的情况下,比如with或者assert语句,再用反斜杠''。
- 在操作符(+-*/等)之前换行:方便阅读时快速理解操作符后面的内容是被如何操作的。
- 空白行:顶层function和class前后各2行,method前后各1行。
- 导入Import:不同的库每个导入各占一行;相同的库里多个模块可以在一行导入。导入顺序:标准库->空白行->第三方库->空白行->本地库。
- 各种复合语句,最好各占一行。为了可读性,不要在一行内出现多个语句。
-
符号
- 引号:string的两侧单引号双引号都可以。string的内容里如果有单引号,外面就用双引号,反之亦然。尽量不使用反斜杠来escape,影响可读性。
- 空白符:
- 各种括号的内部不直接用空白符
Yes: spam(ham[1], {eggs: 2}) No: spam( ham[ 1 ], { eggs: 2 } )
- 逗号分号冒号前面不直接用空白符
但是如果冒号是作为切片(slicing)的符号,两边可以有对称的空白符(当作优先级最低的运算符)。三参数的切片比如[::2],省略参数的地方空白符也要省掉。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]
- function和variable名字和括号之间不加空白符(这个应该都知道吧。。)
- 避免在行尾使用多余的空白符,影响某些符号运行,你又很难发现。
- 运算符两端要各加且只加一个空白符,遇到多层优先级的运算,只在最低优先级的运算符两侧加空白符:
# Yes i = i + 1 submitted += 1 x = x*2 - 1 hypot2 = x*x + y*y c = (a+b) * (a-b) # No 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)
- 末尾逗号:tuple和list的最后一项是可以接逗号的。这里说明一下:如果tuple和list是分行构建的,且每一行最后可以接一个逗号,方便以后的扩展时保持统一格式;如果是在同一行构建的,则没有必要在最后接一个逗号。
-
注释
- 注释时保证注解的内容是保持更新的,不然和代码不符,还真倒不如不注释。
- 注释要说人话,用完整的语句编写。开头大写,句尾加句号(操心命)
- 注释句尾句号后加两个空白符在换行。我没理解有什么用,也许是考虑到markdown的格式
- 大段的注释,每一行用#开头,几个空白符再写内容。段落间的空行也用#开头。(原来不是使用三引号做注释的...)
- 同行注释尽量不使用,干扰阅读代码。如果某些特殊情况必须使用,和代码直接保持两个以上空白符。
- Docstring:给所有的公开的模块,函数,类写doctring。私有的就不用写了,用注释代替。这种注释应该出现在 def 的后一行(我原来都写在前面的..)。默认使用三双引号来做docstring:对于多行的docstring,结尾的三引号要独占一行。
-
命名
- 命名的风格有很多种。Python基本使用的是lower_case_with_underscore
- 使用_single_leading_underscore:表示弱的内部使用。在from X import *是不会导入这一类对象
- 使用single_tailing_underscore_:为了略微改变那些和 Python 的 keyword冲突的命名
- 使用 __double_leading_underscore:我理解是表示强的内部使用,类的外面是无法调用这类对象的。(invoke name mangling)
- 首位都有双下划线的命名只限python内置使用,不要随意起这种名字
- 不要只用小写的‘l’,大写的‘O’和大写的‘I’,容易引起误解
- Package和module的命名用lower_case_with_underscore
- 类的命名使用 CapWords
- 常量的命名应该使用UPPER_CASE
- Class内部的命名,能用非公开就用非公开,因为把一个attribute由非公开变为公开要比反过来容易太多
-
编程过程中的风格建议
- 和None做比较时永远用 is / is not 而不是 ==
- 用 if x is not y 而不是 if not x is y : 更贴近自然语言
- 定义函数的时候,永远用def 而不是 把一个lambda函数赋给一个变量名。lambda函数的唯一使用场景应该是一个复杂的表达里嵌入使用。
- 在使Exceptions的时候,从Exception导出而不是BaseException
- 在使用try...except...排除意外的时候,明确规定 except 的意外情况而不是直接使用‘except:’。这是确保不会把SystemExit和KeyboardInterrupt 算进去,那样的话control-c强退会难以操作。
- 保证Return的一致性:一个函数里要么都不return,要么每种情况的return都明确规定,即便return None
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)
- 永远优先使用string的methods而不是直接使用string module,比如用.startswith(),.endswith()来代替slicing做比较
- 记住空的有序容器比如tuple set string list的布尔值是False
- 永远不要把布尔值和True/False做比较,直接用它的自己的真假来控制流程
有关 function annotation 和 variable annotation 的格式规定,由于我对这些功能本身不了解就先不介绍,如果有需求请直接在原文搜索。心累。