编码风格
模块、类和函数请使用docstring格式注释,谷歌docstring注释
https://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/python_style_rules/#comments
https://python-web-guide.readthedocs.io/zh/latest/codingstyle/codingstyle.html#id2
docstring就是__doc__方法的内容,在模块、函数、类的声明的第一条语句
google风格的docstring
"""
This is a groups style docs.
Parameters:
param1 - this is the first param
param2 - this is a second param
Returns:
This is a description of what is returned
Raises:
KeyError - raises an exception
"""
不要硬编码,不要使用幻数
包导入顺序,标准库、第三方库、本地库;导入模块按照模块导入,按照module.func使用,避免from module import func
异常
尽量少或者尽量精确的使用try/except;少使用print, 多使用logging模块,logging模块可以记录信息但是程序会继续执行
函数相关
docstring;函数参数和返回值尽量使用简单数据类型;函数要么修改传入的可变参数,要么返回一个值,请不要两者同时做;如果默认参数不是immutable对象,最好使用None作为占位符;None是单例模式的,用is 来判断一个对象是否是None;尽量写纯函数(返回结构只依赖于参数并且没有副作用的函数),不容易出错;避免在遍历一个序列的同时修改它,可能会导致非预期的行为;圈复杂度高,没法写单元测试;
类相关:
不要到处OOP,适时使用OOP;业务逻辑代码中禁止使用元类;不要轻易在非__init__方法中添加类属性;多重继承时用MRO顺序;尝试使用CRC(class-responsibility-collaboration):类-职责-交互卡片设计类
测试相关:
一定要写UT,必要的docstring;对于外部调用、网络请求、rpc调用等使用mock或者stub;基于代码行为测试,不要片面追求测试覆盖率。
日志相关:
非必要的地方不要打印日志,日志的作用,记录用户行为,排查问题,记录哪些信息。
ORM和数据库相关:
数据库这一层的接口考虑下参数过滤,防止不恰当参数可能导致的慢查询;优先使用ORM,相比sql语句更容易维护,同时避免了sql注入;
https://github.com/calvinlau/calvinwiki/wiki/MySQL%E6%95%B0%E6%8D%AE%E5%BA%93%E5%BC%80%E5%8F%91%E7%9A%84%E4%B8%89%E5%8D%81%E5%85%AD%E6%9D%A1%E5%86%9B%E8%A7%84
注释相关:
注释有时候能帮助你思考设计,比如如果一个类、函数等难以用一句话描述它的职责,很有可能就违背了SRP(单一职责原则);对于复杂的数据结构,适当注释出类型,` __impl_kwargs = None # type: Dict[str, Any]` ;代码中的特殊注释,TODO表示该处有功能代码待编写,FIXME表示该处代码需要修正,XXX表示该处代码待改进,HACK表示该处代码要调整;
线程安全相关
不能保证线程安全就用线程锁GIL
python代码性能优化
profile,火焰图查看性能瓶颈,代码的耗时遵循2/8定律,大部分web应用瓶颈在IO这里;优先从数据结构、算法、数据库、网络IO等层面优化,大部分web应用语言性能不会成为瓶颈;对于cpu密集的代码可以使用cpython(不是CPython)编写扩展来优化速度,在reddit和知乎都有应用,http://cython.org/;更换语言(切换到golang),框架(使用异步框架),数据库甚至架构,成本较高,作为最后的备选方案;
常见的web后端性能优化方案 :批量,批量接口(比如数据库一次获取多条数据/redis pipeline等),目的是避免多次网络I/O;消除数据库慢查询,索引优化;缓存:使用redis等内存型数据库存热数据,需要注意缓存失效问题(Cashe-aside,Write-through,Write-back),内存型数据库相比较传统关系型数据库速度优势明显,不过难以支持复杂查询;异步:使用celery结合消息队列等把任务交给离线worker执行,防止阻塞当前请求,或者使用异步架构;并发:使用gevent(greenlet)、多线程等并发请求数据,配合gunicorn(master-slave模型)部署。不过需要注意使用gevent mysql driver需要纯python编写的driver才能被monkey patch;多线程、多进程:python虽然有GIL,但是I/O期间会释放GIL,多线程仍可以大幅提升I/O密集应用的性能,多进程使用于CPU密集型应用
命名
注意词性,过程用动宾结构,用返回值的描述命名函数,数据变量使用名词,布尔数据经常使用is等作为前缀,数字类型用cnt作为后缀;适当使用“匈牙利”命名法(能从命名推断类型),使用前缀或后缀;含义精确,具体胜于抽象,不要频繁使用诸如data,info,handle,process等概念太广泛的词汇给变量命名,不要使用偏门的简写,为了代码可读性冗余一些都可以(实际上对于现代语言长命名有一定好处,能减少冲突,容易grep);命名要能凸显出右侧表达式结果的类型和含义;术语表和命名规范;不要自己创造缩写;变量的名称不要和循环里的临时变量名起冲突,一般用下划线定义一个临时使用的变量,比如for 循环
注释与docstring
docstrings = How to use code, 代码约定;Comments = Why & how code works
Docstring应该包括些什么?google风格的docstring,做了什么,传入和传出了什么(参数意义、格式);docstring分为文件的,类的,函数的docstring;意图(目的),解释为什么需要它;描述参数和会抛出的异常;使用注意事项,复杂的使用有demo示例说明;需求文档,使用的api或者github,stackoverflow等链接;尽可能用简洁明了的话给所有的模块、类和函数来几句描述(为什么需要这个模块、类、函数?这个模块、类会在哪里被使用?完成了什么功能?)如果能简单描述出来,说明代码功能明确,无法简单描述的话说明代码可能需要拆分。
注释分为5类;代码的重复,用不同的语言重申代码的内容;代码的解释: 解释复杂的有效的和灵敏的代码,通常有用但是尽可能修改代码使得代码本身更清晰;代码中标记: TODO 标记等,经验表明,往往写了 TODO 后来就一直成了 TODO,所以最好提交代码前把要做的 TODO 做完,TODO 仅仅作为一次代码合并之前的提示。TODO 注释记得加上姓名,日期,联系方式和提示,方便 grep;代码中的总结:简化代码为一句或两句话,这种注释比重复代码更有价值,能帮助人快速理解代码;代码意图的描述:解释代码的目的。意图注释在问题一级上,而不是在答案一级,是一句利用答案的总结描述。『意图注释』和『总结注释』两类注释是可以接受的
异常处理
一般代码中三种错误类型,语法错误,逻辑错误,运行时错误,