【译】Best_Practice_For_Python最佳实践指南
1. 合理组织你的代码库,选择合适的代码管理工具
一个普通的python项目代码库的结构大致包含:
project
project/
__init__.py
__main__.py
core/
utils/
constants/
tests/
docs/
examples/
README.md
LISCENSE
requirements.txt
setup.py
.gitignore
.pylintrc
.flake8
2. 遵循公共的编码风格(code style)
PEP8 -- Python社区圣经,正因为如此,Python社区几乎所有的代码看起来都是一样的风格。
-
文件编码和unicode
- 源码文件使用UTF-8无BOM编码格式
- 使用UNIX \n风格换行符,而不使用dos的\r\n
- py文件头包含:
#!/usr/bin/env python # -*- coding: utf-8 -*-
-
命名
- 小写:函数,方法,变量,包,模块
- 驼峰:类名
- 单下划线开头:受保护方法和内部函数
- 双下划线开头:私有方法
- 全大写:常量
-
模块导入:
- 导入整个模块,而不是模块里的符号(类,函数等),除非第三包有明确说明要导入符号。
理由:这样可以有效避免循环导入。
*个人理解这里有问题,如果py中有额外的函数调用,数据修改动作,import *会隐含被执行,且问题难以发现
- 在文件开头导入模块,按系统模块,第三方模块,本地模型的属性导入。
理由:可以清晰的了解模块的来源。IDE有优化导入的快捷键ctrl+thift+o
-
缩进:
4个空格,不用Tab
-
熟悉常见的约定
要了解
__init__.py, __main__.py
的作用 了解if __name__ == "__main__":
的作用 了解package加载的流程: sys.path, sys.modules的作用 -
注释
参考PEP257,使用reStructuredText和Sphinx可以生成项目文档
-
单行代码不要过长,使用括号进行换行 或
\
换行 -
合理的使用空行,让代码更可读
-
代码尽量扁平,不要嵌套太深
if、for、 while, try/except/finally、with
不要嵌套太多- 方法定义和调用时,参数过多,不合理的换行也会导致代码结构不好看
![1569573820146](file:///C:\Users\W00448~1\AppData\Local\Temp\msohtmlclip1\01\clip_image002.png)
- 尽量多写单元测试:
- 单元要小,测试要快(当然慢也比没有好),通常一个类一个TestCase
- 有了单元测试以后,重构起来,比较放心
- 集成测试:按用例和场景来
3. 立即修复破损的窗户(broken window)
破损的窗户的理论同样适用于编码,当你发现代码有任何问题(不管是坏的设计,错误的决策,还是糟糕的代码),都请立即修复它。
4. 重构你的代码
边开发边重构。写代码时,如果发现有些不妥的地方,要及时重构和修改、测试。
有时会想,先做完再重构,忘掉这种想法。这种想法不可取。越往后拖,越不易重构,越懒得重构。
重构是一个长期持续的活动。
字符串拼接,大量的拼接使用+,性能不高
# py
s = "".join(["Life", "is", "short", "I", "love", "Python"]) # better
s = "Life" + "is" + "short" + "I" + "love" + "Python" # worse
in的使用
# 尽可能的使用in,使用__contains__支持in,加上使用__iter__,可以支持for x in y
for key in d: print(key) # better
for key in d.keys(): print(key) # worse, 不过在遍历的同时需修改字典,可以这样
if key not in d: d[key] = 1 # better
if not key in d: d[key] = 1 # worse
if的使用
if not x: 要好过于:if x == 0, if x == "", if x is None, if x == False
if x: 要好过于 if x != 0, if x is not None, if x != None
或者用if len(x) ?= 0 来判断
if 1 < x < 5 要好过于 if x > 1 and x < 5 # Python运算符支持级联
if x is None 和 if x is not None 要好过于 if x == None 和 if x != None
if x == 1 要好过于 if (x == 1) # 后面的括号是多余的
交换变量的值,不要使用临时变量
a, b = b, a a, b, c, d = d, b, c, a
代码性能优化
-
优化前要进行性能分析,避免盲目的优化,影响了代码的可读性。
-
尽量使用时间复杂度小的算法,一般来说,数据量大的时候,好的算法优势越明显。
-
使用能够正常工作的最简单的方式,比如使用.startsWith就比使用正则匹配好
-
**使用dict或者set来进行元素查找,而不是使用list**
-
使用map/filter来对list中的元素进行处理,而不使用for循环
-
使用列表推导而不是使用for循环, 但是推导里面逻辑复杂,使用for的可读性高
-
尽量减少遍历的趟数,一趟遍历能够处理完,不要遍历多趟。
![1569572715533](file:///C:\Users\W00448~1\AppData\Local\Temp\msohtmlclip1\01\clip_image004.png)
-
需要从redis中拿多个值的时候,可以使用管道,减少IO交互次数。
-
使用多线程或者协程来处理IO密集型任务。
可复用的面向对象设计
- 合理使用组合,继承,混入来进行代码复用
优先使用组合(Has A),其次使用继承(必须是Is A)。使用混入(Mixin)来实现多重继承,能够让代码看起来比较清晰。
搜索平台中三种方式都使用了,比如数据库模型IspTable。算法模型ModelMixin,分词器JiebaTokenizer。
- 合理构建可以复用的工具包,工具类
DBHelper,LogHelper,app.common.utils
-
使用monkey-patch或者继承的方式来扩展第三方包的功能。
如:jieba_wrap中的实现方式
![1569573252739](file:///C:\Users\W00448~1\AppData\Local\Temp\msohtmlclip1\01\clip_image006.png)
多线程,多进程
-
不要在计算密集型任务中使用多线程,没有意义。在IO密集型任务中使用多线程可以提升处理速度。
-
多进程并发写日志到同一个文件中,自带的日志包会有问题,使用concurrent_log_handler
设计模式
合理的使用设计模式,能简化相应的编程任务,是代码清晰可读。
-
装饰器模式: 日志,异常,性能等
-
单例模式:Jieba分词器,按租户粒度隔离
-
发布订阅模式,逻辑解耦
常用类的命名约定
-
前缀: SimpleXXX、DefaultXXX, StandardXXX
-
后缀: XXXService, XXXMixin,XXXHelper, XXXError, XXXException,XXXEnum
-
接口:在声明是多使用XXXable,表示实现类具有该能力,如Runable,Configurable,Customizable,Imutable,Iterable, Cloneable, Serializable...
-
实现类:的声明多使用XXXRunner, XXXConfiguration等名词结构。
5. 创建一致的文档
看起来是个负担,但是合适的文档是项目生命周期中产生整洁代码的基石。Python社区适用下面三个简单的工具或者概念来简化了文档的编写:
-
reStructredText
-
Docstrings
-
Sphinx
通过这三个工具,你可以在Python代码中,编写符合reStructredText语法的注释(Docstrings),然后通过Sphinx这个工具自动生产项目的各种格式的文档(PDF,HTML...)。并且可以把你项目的文档发布到ReadTheDocs文档库中,供别人在线查看。
大量的开源项目都是这种方式维护文档的:
-
flask
-
gunicorn
-
torando
6. 熟练使用PyPI
从PyPI中获取别人的项目,发布你的项目到PyPI中。
pip install requests
7. 参考书籍
• The Python Cookbook
• Fluent Python
• Effective Python: 59 Specific Ways to Write Better Python
8. Python之禅
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
代码美丽胜于丑陋。
Explicit is better than implicit.
显示胜于隐式。
Simple is better than complex.
简单胜于复杂。
Complex is better than complicated.
复杂胜于错综复杂。
Flat is better than nested.
扁平胜于嵌套。
Sparse is better than dense.
稀疏胜于密集。
Readability counts.
可读性非常重要。
Special cases aren't special enough to break the rules.
Although practicality beats purity.
个例不足以特殊到可以打破规则,虽然实用性胜过纯粹性。
Errors should never pass silently.
Unless explicitly silenced.
异常不应该被忽略,除非你显式地这么做。
In the face of ambiguity, refuse the temptation to guess.
模棱两可的时候,不要猜测。
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
应该有一种(最好只有一种)显而易见的方式来做。虽然刚开始那种方式不那么明显。
Now is better than never.
Although never is often better than *right* now.
现在好过于永远不。虽然永远不经常好过于立即。
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
一个实现方式很容易解释,那就是好主意,否则是坏主意。
Namespaces are one honking great idea
命名空间是个很棒的主意。
-- let's do more of those!