python上下文管理器
1.上下文管理的原理
在python2.6以后,当我们操作文件的时候,可以不再使用open打开文件,操作完成之后再close掉文件句柄来实现对文件的操作,而是可以使用with ... as f的方式,无需关闭文件句柄,而是由程序在操作完成之后自动执行close操作;这种在程序开始时执行准备工作,程序代码块结束时再做收尾工作的方式,称为上下文管理;
那么open结合with语句是如何实现上下文管理的呢?查看open的源码得到如下内容:
open return语句调用的是file("/dev/null")的执行返回结果,而在class file中包含如下方法:
def __enter__(self): # real signature unknown; restored from __doc__ """ __enter__() -> self. """ return self def __exit__(self, *excinfo): # real signature unknown; restored from __doc__ """ __exit__(*excinfo) -> None. Closes the file. """ pass
使用dir可以看到类中的所有对象:
['__class__', '__delattr__', '__doc__', '__enter__', '__exit__', '__format__', '__getattribute__', '__hash__', '__init__', '__iter__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'closed', 'encoding', 'errors', 'fileno', 'flush', 'isatty', 'mode', 'name', 'newlines', 'next', 'read', 'readinto', 'readline', 'readlines', 'seek', 'softspace', 'tell', 'truncate', 'write', 'writelines', 'xreadlines']
事实上,在使用with 语句执行class实例化的对象的时候,前期的准备工作(比如打开文件)是由__enter__方法实现的,方法__exit__()完成扫尾工作(比如close文件句柄),而as 后面的对象,是__enter__()方法return的结果,比如看下面的代码:
class MyContext(object): def __init__(self): print "__init__" def __enter__(self): print "open file" return "This is a test" def __exit__(self, exc_type, exc_val, exc_tb): print "close file" def func(self): print "execute" context1 = MyContext() with MyContext() as f: print f 执行结果: __init__ __init__ open file This is a test #f的打印结果 close file
所以,如果我们想要在with语句中使用as 语法,就必须在__enter__()中return self;当然如果不使用as语句,就不需要return self了,with下面的代码块可以执行任意代码;
示例如下:
class MyContext(object): def __init__(self): print "__init__" def __enter__(self): print "open file" return self def __exit__(self, exc_type, exc_val, exc_tb): print "close file" def func(self): print "execute" context1 = MyContext() with MyContext() as f: f.func() 结果为: __init__ __init__ open file execute close file
下面我们做一个小练习:使用python的上下文管理器实现计算斐波拉契数列执行时间的类:
import time class MyTimer(object): def __init__(self,verbose=False): self.verbose = verbose def __enter__(self): self.start = time.time() return self def __exit__(self, exc_type, exc_val, exc_tb): self.end = time.time() self.secs = self.end - self.start self.msecs = self.secs * 1000 if self.verbose: print "cost time:%s ms" %self.msecs def fib(n): if n in [1,2]: return 1 else: return fib(n-1) +fib(n-2) with MyTimer(True): print fib(30)
计算结果如下:
832040 cost time:245.000123978 ms
参考链接:http://python.jobbole.com/82289/
2.使用装饰器实现自定义的上下文管理器
python标准模块contextlib可以作为被修饰函数的装饰器,结合yield实现自定义的上下文管理器,__enter__和__exit__是通过生成器构造的;
在设计模式发布订阅模式中,发布时调用publish将对象传入,完成之后调用flush表明发布完成:
import contextlib class Pipeline(object): def _publish(self): #前期准备工作方法 print "publish the data" def _flush(self): #后期完成之后工作方法 print "flush the mem" @contextlib.contextmanager def publisher(self): try: yield self._publish() finally: self._flush() def execute(self): #with语句中需要被执行的方法 print "exec code" pipeline = Pipeline() with pipeline.publisher() as publisher: pipeline.execute()
执行结果如下:
publish the data exec code flush the mem