zoukankan      html  css  js  c++  java
  • python上下文管理器

    一、上下文管理协议

    上下文管理协议是指:为了让一个对象兼容with语句,必须在这个对象的类中声明__ente__()和__exit__()方法。

    二、上下文管理器

    上下文管理器就是是实现了_ente__()和__exit__()方法的对象

    三、为什么要有上下文管理器?(参考自:https://blog.csdn.net/xiecj_2006/article/details/42748533

    在正常的管理各种系统资源(文件、锁定和连接),在涉及到异常时通常是个棘手的问题。异常很可能导致控制流跳过负责释放关键资源的语句

    filename = 'my_file.txt'
    f = open(filename,'w') 
    f.write('Hello ') 
    f.write('World') 
    f.close()

    如果发生了意外的情况,例如写入'World'时磁盘空间不足,就会抛出异常,那么close()语句将不会被执行。

    一般的解决方案是使用try-finally语句:

    try:
        filename = 'my_file.txt'
        f = open(filename,'w') 
        f.write('Hello ') 
        f.write('World')
    finally:
        f.close() 

    但随着语句的增多,try-finally显然不够简洁,用with-as语句可以很简洁的实现以上功能:

    with open('my_file','w') as f:
        f.write('Hello ')
        f.write('World') 

    这样不仅能处理出现异常的情况,而且还避免了在open一个文件后忘记了写close()方法的情况。

    线程中的锁其实也实现了上下文管理协议:

    import threading
    with threading.Lock():
        # 关键部分 
          statements
        # 关键部分结束

    四、.上下文管理器的实现

    上下文管理器要实现__enter__和__exit__的特殊方法。

    __enter__(self): 进入上下文管理器时调用此方法,其返回值将被放入with-as语句中as说明符指定的变量中。

    __exit__(self,type,value,tb):离开上下文管理器调用此方法。如果有异常出现,type、value、tb分别为异常的类型、值和追踪信息。如果没有异常,

    3个参数均设为None。此方法返回值为True或者False,分别指示被引发的异常得到了还是没有得到处理。如果返回False,引发的异常会被传递出上下文。

     

    文件上下文管理协议大概是如下实现的:

    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,type,value,tb):
            self.f.close()
            return False   #异常会被传递出上下文
    with OpenFile('my_file.txt','w') as f:
        f.write('Hello ')
        f.write('World')

    相当于

    try :
        执行__enter__的内容
    finally:
        执行__exit__的内容

    五、.异常的处理 

    __exit__函数就能够拿到关于异常的所有信息(异常类型,异常值以及异常追踪信息),这些信息将帮助异常处理操作。

    class ListTrans(object):
        def __init__(self,alist):
            self.alist=alist
        def __enter__(self):
            self.thecopy=list(self.alist)
            return self.thecopy
        def __exit__(self,exc_type,value,tb):
            if exc_type is None:
                self.alist[:]=self.thecopy
            return False 

    没有异常发生时:

    alist=[]
    with ListTrans(alist) as working:
        working.append(1)
        working.append(2)
    print alist 

    生成

    [1, 2]

    有异常发生时:

     

    1
    2
    3
    4
    5
    6
    alist=[]
    with ListTrans(alist) as working:
        working.append(1)
        working.append(2)
        raise RuntimeError('we are hosed')
    print alist

    生成:

    1
    RuntimeError: we are hosed

    alist无变化。

    可以捕捉异常:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    alist=[]
    try:
        with ListTrans(alist) as working:
            working.append(1)
            working.append(2)
            raise RuntimeError('we are hosed')
    except RuntimeError as e:
        print e
    print alist

    生成:

    1
    2
    we are hosed
    []

    当然,也可以简单的将__exit__的返回值设为True来忽略异常。

    4.contextmanager装饰器

    @contextmanager

    contextlib模块的contextmanager装饰器可以更方便的实现上下文管理器。

    任何能够被yield关键词分割成两部分的函数,都能够通过装饰器装饰的上下文管理器来实现。任何在yield之前的内容都可以看做在代码块执行前的操作,

    而任何yield之后的操作都可以放在exit函数中。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    from contextlib import contextmanager
    @contextmanager
    def listTrans(alist):
        thecopy=list(alist)
        yield thecopy
        alist[:]=thecopy
    alist=[]
    with listTrans(alist) as working:
        working.append(1)
        working.append(2)
    print alist

    yield返回的值相当于__enter__的返回值。

    要注意的是,这不是异常安全的写法,也就是说,当出现异常时,yield后的语句是不会执行的,想要异常安全,可用try捕捉异常:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    from contextlib import contextmanager
    @contextmanager
    def listTrans(alist):
        thecopy=list(alist)
        try:
            yield thecopy
        except RuntimeError:
            pass
        alist[:]=thecopy
    alist=[]
    with listTrans(alist) as working:
        working.append(1)
        working.append(2)
        raise RuntimeError

    nested与closing

    contextlib模块还有两个好玩的方法:nested,closing。

    nested:用来更方便的减少嵌套写法:

    当要嵌套的写上下文管理器时:

    1
    2
    3
    with open('toReadFile''r') as reader: 
        with open('toWriteFile''w') as writer: 
            writer.writer(reader.read())

    可以用nested简化写法:

    1
    2
    3
    with contextlib.nested(open('fileToRead.txt''r'), 
                           open('fileToWrite.txt''w')) as (reader, writer): 
        writer.write(reader.read())

    python2.7后nested就过时了:

    1
    2
    with open('fileToRead.txt''r') as reader,open('fileToWrite.txt''w') as writer: 
            writer.write(reader.read())

    closing(object):创建上下文管理器,在执行过程离开with语句时自动执行object.close():

    1
    2
    3
    4
    5
    6
    7
    class Door(object) :
        def open(self) :
            print 'Door is opened'
        def close(self) :
            print 'Door is closed'
    with contextlib.closing(Door()) as door :
        door.open()

    在我身后,微笑地活下去吧。
  • 相关阅读:
    digitalpersona 开发
    Task 暂停与继续
    IQueryable 和 IEnumerable(二)
    SpringBoot Redis 订阅发布
    @Formula
    Aop 简单实例
    幂等 zuul的Filter实现
    C# async await 举个栗子
    Integer 类和 int 的区别
    TCP和UDP的区别以及各自应用
  • 原文地址:https://www.cnblogs.com/L-C98/p/9190753.html
Copyright © 2011-2022 走看看