Python 开发规范
下面是一些在常见 Python 通用代码规范之外的实践,带有一定的个人色彩。
Import is hard
import
需要保证顺序,并且合理分块(空行),分块顺序:标准库 > 第三方库 > 内部导入import
语句尽可能在顶层,出现循环引用应该先想办法通过拆分避免循环- 因为这能够监测依赖变动如重命名函数后能够让所有依赖此函数的文件暴露出来
- ES2015+ 强制
export
只能在顶层是个非常好的设计
- 谨慎使用
import as
语句,这会为代码搜索代码困扰- 除非是两种情况:命名无可避免的冲突、快速重构之前的代码
- 禁止使用
import *
- 使用一致的导入语句,避免有歧义的导入
- 项目中混用
import datetime
和from datetime import datetime
是在为自己和别人挖坑
- 项目中混用
- 什么时候使用相对引用什么时候使用绝对引用?
- 优先使用应用级绝对导入路径
- 仅文件夹内使用的文件可以使用相对导入
# good
import io
import json
from django.conf import settings
from django.utils.functional import SimpleLazyObject
from shared.content_type.usecases import ContentTypeUsecase
from shared.papp.usecases import PappUsecase
Explicit is better than implicit
避免制造万能工具
class Object(object):
def __init__(self, **kwargs):
for k, v in kwargs.items():
setattr(self, k, v)
上面这个类是一个万能的类,它可以产生任何你想要的类的实例,甚至可以动态绑定函数作为方法呢,然后呢? 作为其他开发者和作者自己无法从这个类的声明中确定他的任何属性和方法。
避免不确定性的函数
def get_something(data):
type = data.get('type', 'default')
上面犯了好几个错误
data
参数除非你把整个函数以及其调用的函数全部看一遍,否则你永远猜测不到data
到底是什么东西data.get(key, default)
不是一个好的写法,它告诉别人data
是一个不确定的东西,下面单独讲
让代码更加有序
module/class/function
是很好的多级结构,合理利用可以让项目一目了然- 集成自同一父类的子类可以带上相同的后缀,如继承
Api
得到ProductApi
OrderApi
- 单字母(
i
,j
)变量的生命周期尽可能短,合理使用场景是只在单行列表推导使用[i for i in items if i]
配置
- 统一配置文件
- 支持通过环境变量注入配置
- 避免在配置中依赖过多组件
- 密码/密钥信息不要出现在代码和代码配置中
数据校验
对于一般的 Web 程序而言,最大的输入来源是 API 请求,只有/只要我们从 API 层做好数据校验,就可以/才能够保证后端系统的可靠性。
- 为 API 接口提供统一数据校验层
- 确定数据库接口不存在注入风险
- 确定调用命令行不存在注入风险
io 分离
- 文件路径获取方法统一处理,方便扩展和重构
- 网络请求封装处理,封装需要考虑下面的功能
- 代理
- 超时机制
- 参数管理
- 错误处理
- 日志和监控
- 认证/签名
- 负载均衡
- 数据库的封装需要考虑下面的功能
- 事务
- 日志和监控
- 多数据库支持
- 性能分析
集中管理
集中管理的意思是通过一定的手段让系统对象分布更加有序
- 系统一共有多少个X类型的实例,我能不能遍历他们?我能不能自动检测其错误?
- 比如所有 API 继承同一个类,通过元类就能得到系统全部 API 列表
- 系统中有哪些地方访问了数据库/网络?我如何定位出或者统一处理逻辑?
- 如果 io 访问通过合理的接口和封装,这个问题就不难处理了
- 系统中哪些地方访问了哪些路径?迁移服务器需要额外处理哪些东西?
装饰器
- 不要滥用装饰器,代码不是用来炫技的,不是所有场景都适合使用装饰器
- 不要制造万能装饰器,把不同的功能拆分出去
文档
- 使用 sphinx 自动生成文档
- 类型注解也是文档的一部分
- 注意类和函数的结构与文档的对应关系
- 为关键细节提供高亮文档和注释