您的位置:首页 > 编程语言 > Python开发

Python:with语句和上下文管理器对象

2016-09-03 10:49 387 查看
今天看书,书上面提到要尽量使用with自动关闭资源,里面还提到了上下文管理器对象的概念,然后查找资料,对with的解释如下:with的有一些任务,可能事先需要设置,事后做清理工作。对于这种场景,Python的with语句提供了一种非常方便的处理方式。一个很好的例子是文件处理,需要获取一个文件句柄,从文件中读取数据,然后关闭文件句柄。下面就讲下with语句以及上下文管理器对象,希望对你有帮助。
(一)with语句

对于文件操作完成后,应该要关闭它,这是一个常识,因为打开的文件不仅占用了系统资源,而且可能影响其它程序或进程的操作,甚至会导致用户期望与实际操作结果不一样。with语句得语法为
with 表达式 [as 目标]:
代码块
with语句支持嵌套,支持多个with子句,它们两者可以相互转换。"with expr1 as e1,expr2 as e2"与下面的嵌套形式等价
with expr1 as e1:
with expr2 as e2:
with语句使用比较简单。如下面例子不使用with的时候代码如下
f = open('test.txt','w')
f.write("hello")
f.close()#这句很容易被忘记,这也是为什么推荐使用with
使用with语句代码如下:
with open('test.txt','w') as f:
f.write("hello")
with语句可以在代码块执行完毕后,还原到进入该代码块时的现场(这句话要仔细理解,也就是说with里的代码块执行完后,会返回到刚刚进入with时的现场)。with语句代码块执行过程如下:
(1)计算表达式的值,返回一个上下文管理器对象
(2)加载上下文管理器对象的__exit__()方法以备后用
(3)调用上下文管理器对象的__enter__()方法
(4)如果with语句中设置了目标对象,则将__enter__()方法的返回值赋值给目标对象(比如上面的f)

(5)执行with里的代码块
(6)如果步骤(5)代码正常结束,调用上下文管理器对象的__exit__()方法,返回值直接忽略
(7)如果步骤(5)中代码异常,调用上下文管理器对象的__exit__(),并将异常类型、值以及traceback信息作为参数传递给__exit__()方法。如果__exit__()返回值为false,则异常会被重新抛出;如果返回的是true,异常被挂起,程序继续执行
使用with的好处是无论程序以何种方式跳出with块,总能保证资源被正确关闭。下面介绍一下上下文管理器对象。

(二)上下文管理器对象
with的神奇之处得益于一个成为上下文管理器的(context manager)的东西,它用来创建一个这样的对象:它定义程序运行时需要建立的上下文,处理程序的进入和退出,实现上下文管理协议,即在对象中定义__enter__()和__exit__()方法(这两个方法可以重载,这就说明,我们可以自定义属于自己的上下文管理器,待会儿再介绍),其中:
__enter__(self):进入运行时的上下文,也就是进入上下文管理器时调用该函数,返回运行时的上下文对象,with语句中会将这个返回值绑定到目标对象上(上面的例子就是绑定到f上)。顺便说下上下文表达式(Context Expression),上下文表达式指with 语句中跟在关键字 with 之后的表达式,该表达式要返回一个上下文管理器对象,该对象就被赋值给了目标对象。
__exit__(self,exception_type,exception_value,traceback):退出运行时的上下文,定义在块执行(或终止)之后上下文管理器应该做什么。它可以处理异常、清理现场或者处理with块中语句执行完成后需要处理的动作。exception_type,exception_value,traceback三个参数代表的意思分别是异常的类型、值和追踪信息。如果没有异常,3个参数均设为None。此方法返回值为True或者False,分别指示被引发的异常得到了还是没有得到处理。如果返回False,引发的异常会被传递出上下文。这个在前面简单的提到过,希望你能结合上下文仔细理解这些东西。

实际上任何实现了上下文协议的对象都可以称为一个上下文管理器,文件也是实现了这个协议的上下文管理器,它们都能够与with语句兼容。文件对象的__enter__()和__exit__()属性如下
>>>f.__enter__
<built-in method __enter__ of file object at 0x029F0700>
>>>f.__exit__
<built-in method __exit__ of file object at 0x029F0700>


当然我们也可以定义自己的上下文管理器,只要实现了上下文协议便可以和with语句一起使用。如下面例子:
class OpenFile(object):
def __init__(self,filename,mode):
self.filename=filename
self.mode=mode
def __enter__(self):
self.f=open(self.filename,self.mode)
return self.f  #作为as说明符指定的变量的值
def __exit__(self,exception_type,exception_value,traceback):
if exception_type is None:#如果没有异常,正常关闭资源
self.f.close()

else:#有异常发生
print exception_value
print traceback
return False#返回false则异常会被重新抛出

with OpenFile('my_file.txt','w') as f:
f.write('Hello')
f.write('World')
上下文管理器主要作用于资源共享,因此在实际应用中__enter__()和__exit__()方法基本用于资源分配以及释放相关的工作,如打开/关闭文件、异常处理、断开流的连接、锁分配等。为了更好的辅助上下文管理器,Python还提供了contextlib模块,这个下次有机会再讲。技术有限,不正之处,请多多包涵!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  with Python 上下