模块
将相关的类和顶级函数放在同一个模块里1。
导入2
-
仅对包和模块使用导入3
-
推荐使用绝对路径进行导入
import mypkg.sibling from mypkg import sibling from mypkg.sibling import example
-
然而对于复杂包结构,显式相对导入也是可以接受的:
from . import sibling from .sibling import example
-
永远不要使用隐式相对导入
-
-
即使是脚本文件也应该可以导入。代码应该总在执行主程序前检查
if __name__ == '__main__'
,这样当模块被导入时主程序就不会被执行4。def main(): ... if __name__ == '__main__': main()
-
从模块导入类时,通常可以这样导入:
from MyClass import MyClass from foo.bar.YourClass import YourClass
如果导致了本地命名冲突,则这样导入:
import MyClass import foo.bar.YourClass
即使用
MyClass.MyClass
和foo.bar.YourClass.YourClass
-
避免通配符导入
from <module> import *
全局变量5
- 避免使用全局变量
- 例外:
-
脚本的默认选项
-
模块级常量,例如:
PI = 3.14159
。 -
有时使用全局变量来缓存函数所需值或返回值十分有效。
-
- 如果需要,全局变量应该仅在模块内部可用,并通过公共模块级函数进行访问。
列表推导/生成器表达式6
-
简单情况下使用,复杂情况下还是使用循环。
-
每个部分应单独置于一行:映射表达式、
for
语句、过滤器表达式。 -
禁止多重
for
语句或过滤器表达式。
正例:
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 jelly_bean.color == 'black')
反例:
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)
函数和方法
允许使用嵌套/本地/内部函数7
方法和函数内部可再次定义函数。
Lambda 函数8
-
适用于单行函数,如果代码超过 60 至 80个 字符,最好还是定义成常规(嵌套)函数。
-
对于常见操作符,使用
operator
模块中的函数而非Lambda函数。比如乘法操作符,使用operator.mul
而非lambda x, y: x * y
。
函数或方法参数默认值9
不使用可变对象作为函数或方法的参数默认值。
正例:
def foo(a, b=None):
if b is None:
b = []
反例:
def foo(a, b=[]):
...
def foo(a, b=time.time()): # The time the module was loaded???
...
def foo(a, b=FLAGS.my_thing): # sys.argv has not yet been parsed...
...
返回语句一致性10
在函数或方法中,返回语句要么都返回某表达式,要么都不返回。
正例:
def foo(x):
if x >= 0:
return math.sqrt(x)
else:
return None
def bar(x):
if x < 0:
return None
return math.sqrt(x)
反例:
def foo(x):
if x >= 0:
return math.sqrt(x)
def bar(x):
if x < 0:
return
return math.sqrt(x)
装饰器11
避免装饰器的外部依赖(即不要依赖文件、sockets、数据库连接等),因为装饰器运行时这些资源可能不可用。应该保证有效参数调用的装饰器在任何情况下都可成功。
类
允许使用嵌套/本地/内部类7
- 方法、函数或类中可再次定义类。
- 缺点:嵌套类或局部类的实例不能序列化。
属性12
-
对于简单的公有数据,直接使用属性实现,不要使用繁琐的访问器/更改器(accessor/mutator)方法。当需要添加更多功能时,用
@property
装饰器来保持接口一致。但是注意:- 装饰器只适用于新式类。
- 避免让属性访问产生副作用,然而缓存是允许的。
- 避免操作开销太大,属性调用方式容易被认为访问十分轻快。
-
如果子类没有覆盖属性,那么属性继承可能看上去不明显。 因此使用者必须确保访问方法被间接调用,以保证子类中的重载方法被属性调用(使用模板方法设计模式)13。 像这样:
import math class Square(object): """A square with two properties: a writable area and a read-only perimeter. To use: >>> sq = Square(3) >>> sq.area 9 >>> sq.perimeter 12 >>> sq.area = 16 >>> sq.side 4 >>> sq.perimeter 16 """ def __init__(self, side): self.side = side def __get_area(self): """Calculates the 'area' property.""" return self.side ** 2 def ___get_area(self): """Indirect accessor for 'area' property.""" return self.__get_area() def __set_area(self, area): """Sets the 'area' property.""" self.side = math.sqrt(area) def ___set_area(self, area): """Indirect setter for 'area' property.""" self.__set_area(area) area = property(___get_area, ___set_area, doc="""Gets or sets the area of the square.""") @property def perimeter(self): return self.side * 4
-
如果访问内部逻辑更复杂,或访问开销很显著,则应使用类似
get_foo()
和set_foo()
这样的方法14。
类型比较10
对象的类型比较应始终使用 isinstance()
而非直接比较类型。
正例:
if isinstance(obj, int):
反例:
if type(obj) is type(1):
排序10
When implementing ordering operations with rich comparisons, it is best to implement all six operations ( __eq__
, __ne__
, __lt__
, __le__
, __gt__
, __ge__
) rather than relying on other code to only exercise a particular comparison.
To minimize the effort involved, the functools.total_ordering()
decorator provides a tool to generate missing comparison methods.
PEP 207 indicates that reflexivity rules are assumed by Python. Thus, the interpreter may swap y > x
with x < y
, y >= x
with x <= y
, and may swap the arguments of x == y
and x != y
. The sort()
and min()
operations are guaranteed to use the <
operator and the max()
function uses the >
operator. However, it is best to implement all six operations so that confusion doesn’t arise in other contexts.
异常10
-
这样触发异常:
raise MyException("Error message")
或raise MyException
。 -
模块或包应定义自己的特定域的异常基类,此基类应从内建的
Exception
类继承,而不是BaseException
。模块的异常基类应命名为 “Error”15。class Error(Exception): pass
-
尽量减少
try/except
块中的代码量。 -
不要使用
except:
捕获所有异常,也避免捕获Exception
或StandardError
,除非打算重新抛出异常,或者当前已在线程最外层(需要打印错误消息)。 -
当捕获异常到一个变量时,使用
as
而非逗号。例如:
try: raise Error except Error as error: pass
-
When catching operating system errors, prefer the explicit exception hierarchy introduced in Python 3.3 over introspection of
errno
values.
布尔判断
-
不要用
==
或者!=
来比较单件(singletons),比如None
,应该使用is
或is not
10。- 小心使用
if x
表示语义为if x is not None
的情况,比如判断一个默认值为None
的变量或参数是否被设为其它值,该值在布尔语义下可能是false
。
- 小心使用
-
不要用
==
将一个布尔量与true
/false
比较10。正例:
if greeting:
反例:
if greeting == True:
更糟糕:
if greeting is True:
- 如果需要区分
false
和None
,可使用if not x and x is not None:
。
- 如果需要区分
-
对于序列(字符串、列表、元组),利用空序列是隐式
false
的表达10。正例:
if not seq: if seq:
反例:
if len(seq): if not len(seq):
-
处理整数时,使用隐式
false
可能会得不偿失(即不小心将None
当做 0 来处理),可将已知为整型(且不是len()
的返回结果)的值与 0 比较16。正例:
if not users: print 'no users' if foo == 0: self.handle_zero() if i % 10 == 0: self.handle_multiple_of_ten()
反例:
if len(users) == 0: print 'no users' if foo is not None and not foo: self.handle_zero() if not i % 10: self.handle_multiple_of_ten()
-
使用
foo not in bar
而非not foo in bar
17 -
使用
foo is not bar
而非not foo is bar
10
条件表达式18
适用于单行情况(one-liners)比如 x = 1 if cond else 2
,其他情形使用完整的 if
语句。
文件和 sockets19
-
在文件和 sockets(或其他类文件对象)使用完毕时,显式的关闭它。将文件对象的生命周期和文件状态绑定在一起,幻想当文件对象析构时,文件和 sockets 会自动关闭的想法是不现实的,原因如下:
-
运行环境并不确保确实执行文件析构。不同 Python 实现采用不同的内存管理技术,比如延时垃圾回收可能会导致对象生命周期被任意无限制延长。
-
对文件的意外引用会导致其持有时间超出预期(比如对于异常的跟踪,包含有全局变量等)。
-
-
使用
with
语句管理文件。-
无论何时获取或释放资源,上下文管理器应该经由函数或方法进行调用10。
正例:
with conn.begin_transaction(): do_stuff_in_transaction(conn)
反例:
with conn: do_stuff_in_transaction(conn)
-
对于不支持
with
的类似文件的对象,使用contextlib.closing()
:import contextlib with contextlib.closing(urllib.urlopen("http://www.python.org/")) as front_page: for line in front_page: print line
-
字符串20
-
不要使用
+
或+=
循环累积连接字符串,使用"".join()
方法。正例:
items = ['<table>'] for last_name, first_name in employee_list: items.append('<tr><td>%s, %s</td></tr>' % (last_name, first_name)) items.append('</table>') employee_table = ''.join(items)
反例:
employee_table = '<table>' for last_name, first_name in employee_list: employee_table += '<tr><td>%s, %s</td></tr>' % (last_name, first_name) employee_table += '</table>'
-
使用
%
操作符或者"".format()
方法格式化字符串。不过也不能完全一概而论,需要在+
和格式化方式之间好好权衡。正例:
x = a + b x = '%s, %s!' % (imperative, expletive) x = '{}, {}!'.format(imperative, expletive) x = 'name: %s; score: %d' % (name, n) x = 'name: {}; score: {}'.format(name, n)
反例:
x = '%s%s' % (a, b) # use + in this case x = '{}{}'.format(a, b) # use + in this case x = imperative + ', ' + expletive + '!' x = 'name: ' + name + '; score: ' + str(n)
其他
不要编写仅适用于特定 Python 实现的代码10
比如字符串连接语句 a += b
或 a = a + b
的优化甚至在 CPython 中的支持也不够好,因此使用 ''.join()
是更通用的方式。
不要依赖内建类型的原子性21
优先使用 Queue
模块的 Queue
类型作为线程间的数据通信方式。另外,使用 threading
模块及其锁原语(locking primitives)。正确使用条件变量从而以 threading.Condition
取代低级锁。
避免使用威力过大的特性22
比如:
- 元类 (metaclasses)
- 字节码访问 (access to bytecode)
- 任意编译 (on-the-fly compilation)
- 动态继承 (dynamic inheritance)
- 对象父类重定义 (object reparenting)
- 导入 hack (import hacks)
- 反射 (reflection)
- 系统内修改 (modification of system internals)
- 等等
最近修改于 2015-01-24 15:20