一篇 针对Python开发的“最佳实践精选”指南。
总体原则
价值标准
- “为别人开发你也想要使用的工具。” ——Kenneth Reitz
- “简洁性总是胜过可用性。” ——Pieter Hintjens
- “满足90%的使用场景。忽略那些说不的人。” ——Kenneth Reitz
- “优美胜过丑陋。” ——PEP 20
- 为开源(甚至是闭源项目)而开发。
一般开发准则
- “明确胜过含隐含。” —— PEP 20
- “易读亦有价。” —— PEP 20
- “人人都能打补丁。” —— 可汗学院开发文档
- 一旦发现破窗(设计错误,决策失误或编码质量低),马上修补。
- “现在做也要胜过不去做。” —— PEP 20
- “测试要彻底。撰写新功能文档。”
- 人力驱动型开发,比测试驱动型开发更重要。(译者:原文为Even more important that Test-Driven Development–Human-Driven Development,译者认为more important that应该是more important than,应该是作者笔误,否则意思不通,)
- 这些准则可能——应该是很可能——会改变。
特殊准则
风格
感觉合理的话,就遵循PEP 8。
命名
- 变量、函数、方法、包、模块
小写,并使用下划线分隔单词(lower_case_with_underscores)
- 类、异常
首字母大写(CapWords)
- 受保护的方法和内部函数
单下划线开头(_single_leading_underscore(self, …))
- 私有的方法
双下划线开头(__double_leading_underscore(self, …))
- 常量
字母全部大写,单词间用下划线分隔(ALL_CAPS_WITH_UNDERSCORES)
一般命名准则
尽量不要使用只有一个字母的变量名(例如,l,I,O等)。
例外:在很简短的代码块中,如果变量名的意思可以从上下文明显地看出来。
没问题
1 2 |
for e in elements: e.mutate() |
避免冗余描述。
正确的做法
1 2 3 4 |
import audio core = audio.Core() controller = audio.Controller() |
错误的做法
1 2 3 4 |
import audio core = audio.AudioCore() controller = audio.AudioController() |
“反向记法”更好。
正确的做法
1 2 3 |
elements = ... elements_active = ... elements_defunct = ... |
错误的做法
1 2 3 |
elements = ... active_elements = ... defunct_elements ... |
避免使用getter和setter方法。
正确的做法
1 |
person.age = 42 |
错误的做法
1 |
person.set_age(42) |
缩进
用4个空格符——永远别用Tab制表符。就说这么多。
模块引用
引用整个模块,而不是模块中的单个标识符。举个例子,假设一个cantee模块下面,有一个sessions.py文件,
正确的做法
1 2 3 |
import canteen import canteen.sessions from canteen import sessions |
错误的做法
1 2 |
from canteen import get_user # Symbol from canteen/__init__.py from canteen.sessions import get_session # Symbol from canteen/sessions.py |
例外:如果第三方代码的文档中明确说明要单个引用。
理由:避免循环引用。看这里。
把代码引用部分放在文件的顶部,按下面的顺序分成三个部分,每个部分之间空一行。
- 系统引用
- 第三方引用
- 本地引用
理由:明确显示每个模块的引用来源。
文档
遵循PEP 257提出的文档字符串准则。reStructuredText (reST) 和Sphinx有助于确保文档符合标准。
对于功能明显的函数,撰写一行文档字符串。
1 |
"""返回``foo``的路径名.""" |
多行文档字符串应包括:
- 一行摘要
- 合适的话,请描述使用场景
- 参数
- 返回数据类型和语义信息,除非返回
None
“””训练模型,用来对Foo和Bar分类。
用法::
1 2 3 4 5 6 7 8 9 10 11 |
"""Train a model to classify Foos and Bars. Usage:: >>> import klassify >>> data = [("green", "foo"), ("orange", "bar")] >>> classifier = klassify.train(data) :param train_data: `(color, label)`形式的一个元组列表。 :rtype: A :class:`Classifier <Classifier>` """ |
注意
使用主动词(“返回”),而不是描述性的单词(“返回值”)。 在类的文档字符串中为__init__
方法撰写文档。
1 2 3 4 5 6 7 8 9 |
class Person(object): """A simple representation of a human being. :param name: A string, the person's name. :param age: An int, the person's age. """ def __init__(self, name, age): self.name = name self.age = age |
关于注释
尽量少用。与其写很多注释,不如提高代码可读性。通常情况下,短小的方法比注释更有效。
错误的做法
1 2 3 |
# If the sign is a stop sign if sign.color == 'red' and sign.sides == 8: stop() |
正确的做法
1 2 3 4 5 |
def is_stop_sign(sign): return sign.color == 'red' and sign.sides == 8 if is_stop_sign(sign): stop() |
但是的确要写注释时,请牢记:“遵循斯托克与怀特所写的《英文写作指南》。” —— PEP 8
每行的长度
不要过分在意。80到100个字符都是没问题的。
使用括号延续当前行。
1 2 3 4 5 6 |
wiki = ( "The Colt Python is a .357 Magnum caliber revolver formerly manufactured " "by Colt's Manufacturing Company of Hartford, Connecticut. It is sometimes " 'referred to as a "Combat Magnum". It was first introduced in 1955, the ' "same year as Smith &amp; Wesson's M29 .44 Magnum." ) |
测试
尽量争取测试全部代码,但也不必执着于覆盖率。
一般测试准则
- 使用较长的、描述性的名称。通常情况下,这能避免在测试方法中再写文档。
- 测试之间应该是孤立的。不要与真实地数据库或网络进行交互。使用单独的测试数据库,测试完即可销毁,或者是使用模拟对象。
- 使用工厂模式,而不是fixture。
- 别让不完整的测试通过,否则你就有可能忘记。你应该加上一些占位语句,比如
assert False, "TODO: finish me"
。
单元测试
- 每次聚焦一个很小的功能点。
- 测试速度要快,但是速度慢总比不测试好。
- 通常,每一个类或模型都应该有一个测试用例类。
1 2 3 4 5 6 7 8 9 |
import unittest import factories class PersonTest(unittest.TestCase): def |