导语:本文章记录了本人在学习Python基础之控制流程篇的重点知识及个人心得,打算入门Python的朋友们可以来一起学习并交流。
本文重点:
1、掌握if语句之外的else的用法;
2、掌握上下文管理器的定义、协议、使用和with块;
3、掌握有用的@contextmanger装饰器。
一、if语句之外的else块
1、else块介绍
if/else中if和else是同级对立的语句,对立是指流程经过一层if/else语句只能对应一种处理语句。而else在for/else,while/else,try/else语句中的功能则截然不同。后者中的else功能如下:
- for/else:
for循环没有被break语句中止才运行else块。
- while/else:
while循环没有被break语句中止才运行else块。
- try/else:
try块中,没有异常抛出时才运行else块。
下面以for/else为例进行代码实现:
for i in "apple":
if i.isupper():
break
else:
raise ValueError('No upper string was found')
2、try/else块背后的编程风格
try/except不仅用于处理错误,还用于处理错误,这属于EAFP编程风格。
- EAFP:easier to ask for forgiveness than permission
取得原谅比获得许可容易。即先假定存在有效的键或属性,如果假定不成立,那么捕获异常。
- LBYL:look before you leap
三思而后行。即在调用函数或查找属性或键之前显式测试前提条件。
二、上下文管理器和with块
1、上下文管理器介绍
上下文管理器(context manger):在操作文件和建立数据库连接的时候,我们最终需要关闭资源,这就是上下文管理器存在的意义。上下文管理器协议:包含__enter__和__exit__两个方法。
语法:try/finally模式和with语句
实例:try/finally模式
try:
f = open('test.txt', 'a+')
f.write('Foo\n')
finally:
f.close()
接下来我们用with语句进行替换:
实例:with语句
with open('test.txt', 'a+') as f:
f.write('Foo\n')
分析:open 的返回值赋值给变量 f,当离开 with 代码块的时候,系统会自动调用 f.close() 方法。总结:with块的功能在于简化try/finally模式。with语句在开始运行时会在上下文管理器对象上调用__enter__,而with语句结束时会调用__exit__方法。
Tips:with语句中的as语句是可选的,as语句将__enter__返回的值绑定到as语句后的变量。值得注意的是,对于open函数必须加上as字句来获取文件的引用。
2、自定义上下文管理器
在掌握基本上下文管理器和with语句后,我们通过自定义上下文管理器来深刻认识with语句和__enter__以及__exit__的联系。
实例:自定义满足上下文管理器协议的类
class OpenFileDemo(object):
def __init__(self, filename):
self.filename = filename
self.f = open(self.filename, 'a+')
return self.f
def __exit__(self, exc_type, exc_value, traceback):
self.f.close()
if exc_type != SyntaxError:
return True
return False
with OpenFileDemo('test.txt') as f:
f.write('Foo\n')
3、异常处理
当上下文管理器遇到异常时由__exit__方法处理。传给__exit__方法的三个参数如下:
exc_type:异常类(例如SyntaxError)。
exc_value:异常实例。有时会有参数传给异常构造方法,例如错误信息,可以使用exc_value.args获取这些参数。
traceback:traceback对象。
三、@contextmanger装饰器
@contextmanger装饰器是contextlib模块中的工具,它可以将包含yield的语句变成上下文管理器。
其中,yield之前的语句在__enter__方法中执行
,yield之后的语句在__exit__方法执行
,yield后面的值是函数的返回值,绑定到实际调用的with中的as子句的目标变量上
。
如此可以避免编写一个类来实现上下文管理器协议。
实例:@contextmanger装饰器应用之计时器
import contextlib
import time
@contextlib.contextmanager
def timer():
start=time.time()
yield
end=time.time()
usedtime=end-start
print('Running time was %r seconds'%usedtime)
with timer() as usedtime:
time.sleep(1)
注意:一旦with块在调用timer出现异常时,抛出的异常会在timer函数中的yield表达式中再次抛出。如果timer函数没有处理异常的代码就会导致函数运行中止,系统处于无效状态。因此必要时在上下文管理器函数中使用try/finally语句防范错误。
@contextmanger集合了三个不同的Python特性:函数装饰器、生成器和with语句,非常实用!
四、contextlib模块中的实用工具
最后说明contextlib模块中包含的实用工具:
-
closing
: 如果对象提供了 close() 方法,但没有实现 _enter__/__exit_ 协议,那么可以使用这个函数构建上下文管理器。 -
suppress
: 构建临时忽略指定异常的上下文管理器。 -
@contextmanager
: 这个装饰器把简单的生成器函数变成上下文管理器,这样就不用创建类去实现管理器协议了。 -
ContextDecorator
: 这是个基类,用于定义基于类的上下文管理器。这种上下文管理器也能用于装饰函数,在受管理的上下文中运行整个函数。 -
ExitStack
: 这个上下文管理器能进入多个上下文管理器。with 块结束时,ExitStack 按照后进先出的顺序调用栈中各个上下文管理器的_exit_ 方法。如果事先不知道 with 块要进入多少个上下文管理器,可以使用这个类。例如,同时打开任意一个文件列表中的所有文件。
显然,在这些实用工具中,使用最广泛的是 @contextmanager 装饰器,因此要格外留心。这个装饰器也有迷惑人的一面,因为它与迭代无关,却要使用 yield 语句。