阿里山QQ

导航

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

 

  

 

posted on 2017-03-21 15:32  阿里山QQ  阅读(205)  评论(0编辑  收藏  举报