GooglePythonStyleGuide中⽂版
本⽂来⾃:
Python语⾔规范
pychecker
Tip
对你的代码运⾏pychecker
定义:
pychecker是⼀个在Python源代码中查bug的⼯具. 对于C和C++这样的不那么动态的(译者注: 原⽂是less dynamic)语⾔,这些bug通常由编译器来捕获. pychecker和lint类似. 由于Python的动态特性, 有些警告可能不对. 不过伪告警应该很少.
优点:
可以捕获容易忽视的错误, 例如输⼊错误, 使⽤未赋值的变量等.
缺点:
pychecker不完美. 要利⽤其优势, 我们有时侯需要: a) 围绕着它来写代码 b) 抑制其告警 c) 改进它, 或者d) 忽略它.
结论:
确保对你的代码运⾏pychecker.
关于如何运⾏pychecker的更多信息, 参考
你可以设置⼀个叫做__pychecker__的模块级变量来抑制适当的告警. 例如:
__pychecker__ = 'no-callinit no-classattr'
采⽤这种抑制⽅式的好处是我们可以轻松查抑制并回顾它们.
你可以使⽤ pychecker --help 来获取pychecker告警列表.
要抑制”参数未使⽤”告警, 你可以⽤”_”作为参数标识符, 或者在参数名前加”unused_”. 遇到不能改变参数名的情况, 你可以通过在函数开头”提到”它们来消除告警. 例如:
def foo(a, unused_b, unused_c, d=None, e=None):
(d, e) = (d, e) # 让pychecker不告警
return a
理想情况下, 我们以后会扩展pychecker以确保你真的没有使⽤这些参数.
导⼊
Tip
仅对包和模块使⽤导⼊
定义:
模块间共享代码的重⽤机制.
优点:
命名空间管理约定⼗分简单. 每个标识符的源都⽤⼀种⼀致的⽅式指⽰. x.Obj表⽰Obj对象定义在模块x中.
缺点:
模块名仍可能冲突. 有些模块名太长, 不太⽅便.
结论:
使⽤ import x 来导⼊包和模块.
使⽤ from x import y , 其中x是包前缀, y是不带前缀的模块名.
使⽤ from x import y as z, 如果两个要导⼊的模块都叫做z或者y太长了.
例如, 模块 ho 可以⽤如下⽅式导⼊:
from sound.effects import echo
...
echo.EchoFilter(input, output, delay=0.7, atten=4)
导⼊时不要使⽤相对名称. 即使模块在同⼀个包中, 也要使⽤完整包名. 这能帮助你避免⽆意间导⼊⼀个包两次.
包
Tip
使⽤模块的全路径名来导⼊每个模块
优点:
避免模块名冲突. 查包更容易.
缺点:
部署代码变难, 因为你必须复制包层次.
结论:
所有的新代码都应该⽤完整包名来导⼊每个模块.
应该像下⾯这样导⼊:
# Reference in code with complete name.
import ho
# Reference in code with just module name (preferred).
from sound.effects import echo
异常
Tip
允许使⽤异常, 但必须⼩⼼
定义:
异常是⼀种跳出代码块的正常控制流来处理错误或者其它异常条件的⽅式.
优点:
正常操作代码的控制流不会和错误处理代码混在⼀起. 当某种条件发⽣时, 它也允许控制流跳过多个框架. 例如, ⼀步跳出N个嵌套的函数, ⽽不必继续执⾏错误的代码.
缺点:
可能会导致让⼈困惑的控制流. 调⽤库时容易错过错误情况.
结论:
异常必须遵守特定条件:
1. 像这样触发异常: raise MyException("Error message") 或者 raise MyException . 不要使⽤两个参数的形式( raise
MyException, "Error message" )或者过时的字符串异常( raise "Error message" ).
2. 模块或包应该定义⾃⼰的特定域的异常基类, 这个基类应该从内建的Exception类继承. 模块的异常基类应该叫
做”Error”.
class Error(Exception):
pass
3. 永远不要使⽤ except: 语句来捕获所有异常, 也不要捕获 Exception 或者 StandardError , 除⾮你打
算重新触发该异常, 或
者你已经在当前线程的最外层(记得还是要打印⼀条错误消息). 在异常这⽅⾯, Python⾮常宽容, except: 真的会捕获包括
Python语法错误在内的任何错误. 使⽤ except: 很容易隐藏真正的bug.
4. 尽量减少try/except块中的代码量. try块的体积越⼤, 期望之外的异常就越容易被触发. 这种情况下, try/except块将隐藏
真正的错误.
5. 使⽤finally⼦句来执⾏那些⽆论try块中有没有异常都应该被执⾏的代码. 这对于清理资源常常很有⽤, 例如关闭⽂件.
全局变量
Tip
避免全局变量
定义:
定义在模块级的变量.
优点:
偶尔有⽤.
缺点:
导⼊时可能改变模块⾏为, 因为导⼊模块时会对模块级变量赋值.
结论:
避免使⽤全局变量, ⽤类变量来代替. 但也有⼀些例外:
1. 脚本的默认选项.
2. 模块级常量. 例如: PI =
3.14159. 常量应该全⼤写, ⽤下划线连接.
3. 有时候⽤全局变量来缓存值或者作为函数返回值很有⽤.
4. 如果需要, 全局变量应该仅在模块内部可⽤, 并通过模块级的公共函数来访问.
嵌套/局部/内部类或函数
Tip
⿎励使⽤嵌套/本地/内部类或函数
定义:
类可以定义在⽅法, 函数或者类中. 函数可以定义在⽅法或函数中. 封闭区间中定义的变量对嵌套函数是只读的.
优点:
允许定义仅⽤于有效范围的⼯具类和函数.
缺点:
嵌套类或局部类的实例不能序列化(pickled).
结论:
推荐使⽤.
列表推导(List Comprehensions)
Tip
python官方文档中文版可以在简单情况下使⽤
定义:
列表推导(list comprehensions)与⽣成器表达式(generator expression)提供了⼀种简洁⾼效的⽅式来创建列表和迭代器, ⽽不必借助map(), filter(), 或者lambda.
优点:
简单的列表推导可以⽐其它的列表创建⽅法更加清晰简单. ⽣成器表达式可以⼗分⾼效, 因为它们避免了创建整个列表.
缺点:
复杂的列表推导或者⽣成器表达式可能难以阅读.
结论:
适⽤于简单情况. 每个部分应该单独置于⼀⾏: 映射表达式, for语句, 过滤器表达式. 禁⽌多重for语句或过滤器表达式. 复杂情况下还是使⽤循环.
No:
result = [(x, y) for x in range(10) for y in range(5) if x * y > 10]
return ((x, y, z)
for x in xrange(5)
for y in xrange(5)
if x != y
for z in xrange(5)
if y != z)
Yes:
result = []
for x in range(10):
for y in range(5):
if x * y > 10:
result.append((x, y))
for x in xrange(5):
for y in xrange(5):
if x != y:
for z in xrange(5):
if y != z:
yield (x, y, z)
return ((x, complicated_transform(x))
for x in long_generator_function(parameter)
if x is not None)
squares = [x * x for x in range(10)]
eat(jelly_bean for jelly_bean in jelly_beans
if lor == 'black')
默认迭代器和操作符
Tip
如果类型⽀持, 就使⽤默认迭代器和操作符. ⽐如列表, 字典及⽂件等.
定义:
容器类型, 像字典和列表, 定义了默认的迭代器和关系测试操作符(in和not in)
优点:
默认操作符和迭代器简单⾼效, 它们直接表达了操作, 没有额外的⽅法调⽤. 使⽤默认操作符的函数是通⽤的. 它可以⽤于⽀持该操作的任何类型.
缺点:
你没法通过阅读⽅法名来区分对象的类型(例如, has_key()意味着字典). 不过这也是优点.
结论:
如果类型⽀持, 就使⽤默认迭代器和操作符, 例如列表, 字典和⽂件. 内建类型也定义了迭代器⽅法. 优先考虑这些⽅法, ⽽不是那些返回列表的⽅法. 当然,这样遍历容器时,你将不能修改容器.
Yes: for key in adict: ...
if key not in adict: ...
if obj in alist: ...
for line in afile: ...
for k, v in dict.iteritems(): ...
No: for key in adict.keys(): ...
if not adict.has_key(key): ...
for line adlines(): ...
⽣成器
Tip
按需使⽤⽣成器.
定义:
所谓⽣成器函数, 就是每当它执⾏⼀次⽣成(yield)语句, 它就返回⼀个迭代器, 这个迭代器⽣成⼀个值. ⽣成值后, ⽣成器函数的运⾏状态将被挂起, 直到下⼀次⽣成.
优点:
简化代码, 因为每次调⽤时, 局部变量和控制流的状态都会被保存. ⽐起⼀次创建⼀系列值的函数, ⽣成器使⽤的内存更少.
缺点:
没有.
结论:
⿎励使⽤. 注意在⽣成器函数的⽂档字符串中使⽤”Yields:”⽽不是”Returns:”.
Lambda函数
Tip
适⽤于单⾏函数
定义:
与语句相反, lambda在⼀个表达式中定义匿名函数. 常⽤于为map()和filter()之类的⾼阶函数定义回调函数或者操作符.
优点:
⽅便.
缺点:
⽐本地函数更难阅读和调试. 没有函数名意味着堆栈跟踪更难理解. 由于lambda函数通常只包含⼀个表达式, 因此其表达能⼒有限.
结论:
适⽤于单⾏函数. 如果代码超过60-80个字符, 最好还是定义成常规(嵌套)函数.
默认参数值
Tip
适⽤于⼤部分情况.
定义:
你可以在函数参数列表的最后指定变量的值, 例如, def foo(a, b = 0): . 如果调⽤foo时只带⼀个参数, 则b被设为0. 如果带两个参数, 则b的值等于第⼆个参数.
优点:
你经常会碰到⼀些使⽤⼤量默认值的函数, 但偶尔(⽐较少见)你想要覆盖这些默认值. 默认参数值提供了⼀种简单的⽅法来完成这件事, 你不需要为这些罕见的例外定义⼤量函数. 同时, Python也不⽀持重载⽅法和函数, 默认参数是⼀种”仿造”重载⾏为的简单⽅式.
缺点:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论