编写高质量Python代码
摘自 《编写高质量代码改善 Python 程序的 91 个建议》
使用with自动关闭资源
上下文管理器(context manager)
在对象中定义 __enter__()
和__exit__()
方法
__enter__()
:进入运行时的上下文,返回运行时上下文相关的对象,with语句会叫这个返回值绑定到目标对象。
__exit__(exception_type, exception_value, traceback)
:退出运行时的上下文,定义在块执行(或终止)之后上下文管理器应该做什么。可以处理异常、清理现场或者处理处理with语句执行完成之后需要处理的动作。
Python中提供的contextlib模块
contextlib模块通过生成器实现,其中的contextmanager作为装饰器来提供一种针对函数级别上的上下文管理机制。可直接作用于函数/对象而不用关心__enter__()
和__exit__()
方法的具体实现。
使用else子句简化代码
循环语句
当循环自然终结(循环条件为假)时else从句会被执行一次,而当循环是由break语句中断时,else子句不会被执行。
栗子:
def print_prime(n):
for i in range(2, n):
found = True
for j in range(2, i):
if i % j == 0:
found = False
break
if found:
print('%d is a prime number'%i)
def print_prime2(n):
for i in range(2, n):
for j in range(2, i):
if i % j == 0:
break
else:
print('%d is a prime number')
以上两个函数的执行完全一样。
异常处理
Python的异常处理中有一种try-except-else-finally
形式。else
子句在try无异常时执行。
举栗:
def save(db, obj):
# some_error_occurred = False
try:
db.execute('a sql stmt', obj.attr1)
db.execute('a sql stmt2', obj.attr2)
except DBError:
db.rollback()
else:
db.commit()
如果没有else子句,就要通过异常标识来判断是否进行db.commit()
。
异常处理
异常处理流程
在异常语句中,except
异常捕获会出现多次,else
子句在没有异常时执行,finally
子句不管有没有异常都会执行。
其中,在except
语句和else
语句中再有异常产生时,会继续执行finally
语句;而finally
中继续有异常发生,会向上层抛出,处理不当会导致前面抛出的异常丢失。
异常处理遵循的原则
- 注意异常的粒度,不推荐在try中放入过多的代码。注意保持异常粒度的一致性和合理性。
- 谨慎使用单独的except语句处理所有异常,最好能定位具体异常。单独的except语句的使用,真实的错误往往被掩盖。
- 注意异常捕获的顺序,在合适的层次处理异常。应该将子类异常在前面的except语句中抛出,父类异常在后面的except语句抛出。Python中使用BaseException作为所有异常的基类。
- 使用更为友好的异常消息,遵循异常参数的规范。根据异常阅读者(开发者/用户),选择合适的异常消息。
Python中的内建异常继承结构
finally中可能发生的陷阱
未被处理的异常会被临时保存起来,当finally执行完毕的时候,临时保存的异常将会再次被抛出。但如果finally语句中产生了新的异常或者执行了return或者break语句,那么临时保存的异常将会被丢失,从而导致异常屏蔽。
对于有返回语句的异常处理,如果存在finally语句,那么在执行return之前,会先执行finally中的语句,此时如果finally语句中return语句,则前边的return语句会被屏蔽。
开发过程中,应该避免在finally语句中使用break和return语句。
判断对象是否为空
Python中为空的对象:
- 常量 None,False
- 任何形式的数值类型零,如0,0L,0.0,0j
- 空的序列,如(),[],空字符串
- 空的字典,如{}
- 当用户定义的类中定义了
__nonzero__()
方法和__len__()
方法,并且该方法返回整数0或者布尔值False。
其中常量None的特殊性体现在它既不是0,False,也不是空字符串,就是一个空值对象。其数据类型为NoneType
,遵循单例模式,是唯一的。所有赋值为None的变量都相等,None与任何其他非None对象比较结果都为False。
注:float(‘nan’)为True。
自定义对象的非空判断
__nonzero__()
方法用于对自身对象进行空值测试,返回0/1或者True/False。如果一个对象没有定义该方法,Python将获取__len__()
方法调用的结果来进行判断,__len__()
返回值为0则表示为空。
如果一个类中既没有定义__len__()
方法也没有定义__nonzero__()
,该类的实例用if判断的结果都为True。