Python 代码规范小结
code review中一些小结, 还没来得及加例子, 简要记录, 供参考
law
一: 一切都与复杂度有关 二: 代码应当易于理解
对人:
“好程序员”应当竭尽全力, 把程序写得让其他程序员(以及以后的自己)容易理解.
对代码:
- 代码被阅读的次数远多于编写和修改的次数
- E = mc2 (Error = more codes)
对项目:
公式: 可行性=(当前价值+未来价值)/(实现成本+维护成本). 即相比降低实现成本, 降低维护成本更加重要
基础: 风格
团队成员遵守统一的风格, 保持风格的一致性, 减少理解难度
遵循基础的编码风格:
请仔细阅读, 使用对应编辑器插件工具协助检查
-
遵循 pep8 风格 利用pep8工具(编辑器相关插件)来解决这个问题, 在review之前处理. 以避免在review过程中出现此类问题.
-
遵循 Google Code Style / 中文版
-
不要吝啬空行, 把相关的代码行分组, 形成代码块. 声明按块组织起来, 并且把代码分成”段落”(按步骤/顺序/逻辑结构分), 排版合理
-
每行只写一个语句, 每行只声明一个变量
注释
注释应该有很高的
价值
(传递信息/空间占用)
-
代码本身应该尽力做到自说明
-
注释, 记录了在写代码过程中的思考, 保持紧凑, 简单准确的描述
-
不要使用尾注释. 容易被整行拷贝/不容易被编辑修改/逐渐腐烂
x = 1 # bad comment # good comment x = 1
-
不需要的代码, 维护到版本库后(写明
commit info
), 然后删除. 不要注释起来 -
不要给不好的命名加注释, 应该去修改命名
-
不要给那些从代码本身就能
快速
推断出来的事实写注释.(不要为了注释而注释) -
对于复杂的计算逻辑, 要给出注释, 可以通过列举例子, 简单的输入输出来描述
-
对于大段的逻辑或模块, 需要给总结性注释
-
注释代码时, 应注重-为何做, 而不是-怎么做
-
每行注释前用一个空行分开. 注释缩进要和相应代码一致
命名
把信息装入名字中.(自说明)
- 尽量短, 但是要包含足够的信息(刨掉其中毫无意义的词)
- 命名一定要有意义, 尽量少使用单个字符作为命名, 除非短表达式(列表解析/lambda等)以及小的作用域范围
- 常量大写, 变量小写, 类名驼峰, 函数名小写加下划线, 不要混用下划线和驼峰.
- 不要使用关键字命名, 例如
type
和dir
- 避免使用容易混淆的命名, 防歧义
- 慎用首字母缩略词和缩写, 除非团队成员都理解(不要妄图用注释来解决这个问题, 即, 不要注释不好的命名)
- 不要使用大小写来区分不同对象
- 同一个变量, 在多个地方, 前后端/数据库/不同函数/请求等, 尽量保持命名一致性
- 不要害怕过长的命名, 保证易于理解和阅读(现代编辑器可以搞定自动补全和批量变更的问题)
- 使用具体的名字, 而不是泛化的名字, 例如
params/args
等, 没有隐含任何信息 bool
类型, 除非名字本身有True/False
的含义, 否则建议统一使用is_
前缀- 不要使用双重否定的命名:
is_not_pass
for a in b
, 注意a
和b
的单复数区分
常量
- 常量大写
- 作用于同一个模块/逻辑的多个常量, 建议使用统一的前缀
- 将常量统一组织到某个文件/某几个文件, 并写明注释.
- 函数/循环中的正则, 请预先
compile
, 放入变量中. - 善用
Enum
, 对可读性提升很大 - 同一个枚举变量中, 其包含类型应当一致
变量
- 减少变量: 变量越多, 越难全部追踪其动向. a.减少没有价值的中间变量 b.减少中间结果(可以通过
提前返回
来消除) c.减少控制流变量 - 缩小变量作用域: 避免全局变量(命名空间污染). 需要做到让你的变量对尽量少的代码行可见.
- 变量定义尽量靠近其使用的地方, 或者, 在使用时定义.
- 不要使用
import *
, 会出现各种突如其来
的变量名, 可能导致名字空间污染, 造成诡异问题
数据结构
dict
, 不要使用for key in d.keys()
, 直接使用for key in d
表达式
原则: 保持简短, 易懂.(拆分超长表达式)
- 抽取反复出现的长表达式到变量或者函数调用
- 使用解释变量, 将超长表达式中的自表达式抽取城一个解释变量.(抽取, 然后使用变量, 而不是每次都重复表达式)
- 总结变量: 一个表达式不需要解释, 但是装入一个新的变量中仍然有用. 短名字替代一大块代码. 例如:
numbers[0]['obj'].name
- 使用摩根定理:
not a and not b
tonot (a or b)
- 删除公共子表达式:如果发现某个表达式老是在你面前出现,就把它赋值给一个变量
- 中文, 请统一使用
u"中文"
- 表达式中避免使用
魔数
, 使用常量/枚举替代之
控制流: 分支
if/else
顺序: a. 先处理正逻辑而不是负逻辑. b. 先处理掉简单的情况, 还能保证if/else在同一个屏幕内都可见(否则到了else
需要回头查) c.先处理有趣或可疑的逻辑return early
, 从函数中提前返回. 使用guard clause
来实现. 某些情况返回后, 将不必要思考某个分支出口, 剩余注意力集中在为数不多的情况. 另一个好处是, 能有效减少代码缩进.- 减少嵌套: 嵌套很深的代码很难理解, 每个嵌套层次会在读者’思维栈’上又增加了一个条件. 使用
return early
来减少嵌套. 而循环中的减少嵌套方式, 可以使用if condition: continue/break
来进行提早返回
. - 减少嵌套: 当你对代码进行改动的时候, 从全新的角度审视它, 把它作为一个整体来看待.只关心局部, 不敢动旧有代码, 很容易一层层逻辑嵌套往里加导致深层嵌套
- 使用
is
来判定是否是None
, 而不是==
- 条件语句中参数顺序: 左侧变量, 右侧字面值/常量
- 默认情况都使用
if...else
, 三目运算只有在最简单的情况下才使用 if condition: return
则不需要else
- 注意
if/else
的多层嵌套, 在某些情况下, 判断条件中恒真/恒假的情况
控制流: 循环
- 善用
enumerate
而不是维护index
变量(enumerate
还可以从1开始计数) - 除非必要(逻辑确实如此且带
break
), 否则不要使用for...else
.(增加理解成本) - 不要使用
for _ in l: _.x
, 可读性太差 - 减少循环内的
if...else...
嵌套层次, 可以使用if condition: continue
控制流: 异常处理
异常日志同注释, 应该有很高的
价值
(传递信息/空间占用)
- 不要把所有代码放到
try except
中, 只捕获会出异常的代码片段. 注意粒度, 不要放入不必要的代码 - 不要吞掉异常, 处理或抛出, 同时要打日志(使用
logging
而不是print
打日志) - 谨慎使用
except Exception
捕获所有异常. - 不要在
finally
语句中使用return进行返回, 有坑. - 异常的错误信息要
有用
, 即足够明确, 对问题排查有帮助. - 不要使用异常控制程序的流程. 滥用异常, 异常不应该处理正常逻辑
- 不要滥用异常, 底层被调用函数早已
try...except
处理了, 调用方不需要再次处理
函数
函数不要太大, 嵌套不要太深
- 参数命名的一致性: 多个参数, 选择一个有意义的顺序, 并始终一致地使用它(可读性更好, 更容易发现问题)
- 不要使用可变对象作为函数默认参数的值
- 一次只做一件事, 注意函数大小, 注意抽象/拆分
- 抽取不相关的子问题到独立的函数中, 例如纯工具代码, 通用代码, 项目专属代码
- 抽取反复出现重复的代码到独立函数中
return
值不要使用0/1
来代表True/False
- 同一个函数可能存在多个
return
, 返回值要保持一致(个数/类型) return early
, 减少阅读代码时的逻辑堆积, 减少贯穿函数始终用于最终判断return的变量数量. 超过3个就变得有些难以维护了, 阅读过程中确定其值有困难- 如果函数调用链中, 参数或者return的值反复出现pack/unpack, 可以考虑用
dict
封装来进行传递. - 如果发现每次调用一个函数后, 还需要对返回值进行二次处理, 则是函数封装得不够导致的. 需重构函数, 将处理加进去. (防止某次调用忘了二次处理导致的bug)
类
- 不要使用type进行类型检查, 用
isinstance
- 使用新式类, 驼峰命名
- 假设类的某个属性, 每次取出来都需要进行处理(格式化, 转换等, 例如日期格式), 使用
property
封装这层处理, 同时处理异常情况.
模块
import
顺序: 标准库/第三方库/本项目, 之间使用空行隔开- 多行
import
, 请使用from a import (b, c, d)
而不是\
来进行换行 - 不要使用
from A import *
- 当相对独立的多个逻辑代码混杂放在一起, 或者发现constant文件超大包含了大量不同逻辑的产量, 可以考虑模块切分
抽象
- 一定不要机械地复制粘贴代码(会出现大量的重复代码), 应该从全局考虑是否可以抽象
- 多个函数之间, 如果仅有一两行代码不同, 则可以进行抽象提取
- 当一段相似的代码出现两次以上, 需要考虑封装(注意粒度)
设计
软件设计三大误区: 1.编写不必要的代码 2.代码难以修改 3.过分追求通用
- 思考足够充分, 减少过度设计
其他
- 熟悉标准库, 减少土制轮子的概率, 可以少写代码
- 熟悉框架/优秀第三方库提供的接口及特性, 原因同上
- 项目发布前, 移除所有
print
语句 - 文件/函数是否写明作者信息? 不, 版本记录中有作者信息. 容易形成
领地
, 他人不敢修改/不敢大改, 容易造成代码腐烂. 占用空间且没啥用.
参考书目
- 编写可读代码的艺术
- 简约之美—软件设计之道
- 编写高质量代码—改善Python程序的91个建议