剑客
关注科技互联网

Python 代码规范小结

code review中一些小结, 还没来得及加例子, 简要记录, 供参考

law

一: 一切都与复杂度有关

二: 代码应当易于理解

对人:

"好程序员”应当竭尽全力, 把程序写得让其他程序员(以及以后的自己)容易理解.

对代码:

  1. 代码被阅读的次数远多于编写和修改的次数
  2. 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
    to not (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个建议
分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址