• 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
    

      

  • 相关阅读:
    在每个类声明之后、每个函数定义结束之后都要加空行。
    不提倡使用全局变量
    头文件中只存放“声明”而不存放“定义”
    用 #include “filename.h” 格式来引用非标准库的头文件
    用 #include <filename.h> 格式来引用标准库的头文件
    为了防止头文件被重复引用
    java Excel导入、自适应版本、将Excel转成List<map>对象
    selenium用java找到表格某一行某一列中含有特定文字的某个元素
    关于java中创建文件,并且写入内容
    java把一个文件的内容复制到另外一个文件
  • 原文地址:https://www.cnblogs.com/cqq-20151202/p/6594603.html
Copyright © 2020-2023  润新知