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
浙公网安备 33010602011771号