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

    术语

    要使用 with 语句,首先要明白上下文管理器这一概念。有了上下文管理器,with 语句才能工作。

    下面是一组与上下文管理器和with 语句有关的概念。

    上下文管理协议(Context Management Protocol):包含方法 __enter__() 和 __exit__(),支持

    该协议的对象要实现这两个方法。

    上下文管理器(Context Manager):支持上下文管理协议的对象,这种对象实现了

    __enter__() 和 __exit__() 方法。上下文管理器定义执行 with 语句时要建立的运行时上下文,

    负责执行 with 语句块上下文中的进入与退出操作。通常使用 with 语句调用上下文管理器,

    也可以通过直接调用其方法来使用。

    运行时上下文(runtime context):由上下文管理器创建,通过上下文管理器的 __enter__() 和

    __exit__() 方法实现,__enter__() 方法在语句体执行之前进入运行时上下文,__exit__() 在

    语句体执行完后从运行时上下文退出。with 语句支持运行时上下文这一概念。

    上下文表达式(Context Expression):with 语句中跟在关键字 with 之后的表达式,该表达式

    要返回一个上下文管理器对象。

    语句体(with-body):with 语句包裹起来的代码块,在执行语句体之前会调用上下文管

    理器的 __enter__() 方法,执行完语句体之后会执行 __exit__() 方法。

    contextlib 模块

    contextlib 模块提供了3个对象:装饰器 contextmanager、函数 nested 和上下文管理器 closing。使用这些对象,可以对已有的生成器函数或者对象进行包装,加入对上下文管理协议的支持,避免了专门编写上下文管理器来支持 with 语句。

    装饰器 contextmanager

    contextmanager 用于对生成器函数进行装饰,生成器函数被装饰以后,返回的是一个上下文管理器,其 __enter__() 和 __exit__() 方法由 contextmanager 负责提供,而不再是之前的迭代子。被装饰的生成器函数只能产生一个值,否则会导致异常 RuntimeError;产生的值会赋值给 as 子句中的 target,如果使用了 as 子句的话。


    示例1代码:

    #-*- coding:utf-8 -*-
    import contextlib
    import Queue
    
    q=Queue.Queue(5)
    
    
    @contextlib.contextmanager
    def work(li,val):
        #追加val到列表
        li.append(val)
        try:
             #遇到yield函数将切回with方法执行print(li)
             yield
        finally:
            li.remove(val)
    
    li=[]
    q.put("aaa")
    
    #进入上下文管理器
    with work(li,"renwu"):#这里由work方法先处理
        print(li)
        q.get()#获取队列中的任务,获取到后with将继续执行work方法中yield下半部分代码li.remove(val)
    print(li)
    View Code

    利用yield返回值来处理

     1 #-*- coding:utf-8 -*-
     2 import contextlib
     3 import Queue
     4 
     5 @contextlib.contextmanager
     6 def myopen(file,mode):
     7     f=open(file,mode,encoding="utf-8")
     8     try:
     9         yield f
    10     finally:
    11         f.close()
    12 
    13 #as f = yield f ,yield的返回一个f对象 
    14 with myopen('xxx.txt','r') as f:
    15     print(f.readline())
    myopen

    示例2代码(class实现):

     1 #-*- coding:utf-8 -*-
     2 import contextlib
     3 import Queue
     4 
     5 q=Queue.Queue(5)
     6 
     7 #自定义类
     8 class work_with(object):
     9     def __init__(self,li,val):
    10         self.val=val
    11         li.append(val)
    12 
    13 
    14     def hello(self):
    15         print("hello")
    16 
    17     #__enter__  这个结果是赋给 as f
    18     def __enter__(self):
    19         return self
    20 
    21 
    22     #*argv 会传递三个值
    23     #with最后执行的是 __exit__方法
    24     def __exit__(self,*argv):
    25         print(argv)
    26         li.remove(self.val)
    27 
    28 
    29 
    30 li=[]
    31 q.put("aaa")
    32 
    33 #with直接调用类的方式执行
    34 with work_with(li,"hello") as f:
    35     f.hello()
    36     q.get()
    View Code
    with expression as variable:
        with block

    该代码快的执行过程是: * 1.先执行expression,然后执行该表达式返回的对象实例的__enter__函数,然后将该函数的返回值赋给as后面的变量。(注意,是将__enter__函数的返回值赋给变量) * 2.然后执行with block代码块,不论成功,错误,异常,在with block执行结束后,会执行第一步中的实例的__exit__函数。)

    函数 nested

    nested 可以将多个上下文管理器组织在一起,避免使用嵌套 with 语句。

    清单 11. nested 语法
    with nested(A(), B(), C()) as (X, Y, Z):
         # with-body code here

    类似于:

    . nested 执行过程
    with A() as X:
        with B() as Y:
            with C() as Z:
                 # with-body code here

    需要注意的是,发生异常后,如果某个上下文管理器的 __exit__() 方法对异常处理返回 False,则更外层的上下文管理器不会监测到异常。

    上下文管理器 closing

    closing 的实现如下:

    上下文管理 closing 实现

    class closing(object):
        # help doc here
        def __init__(self, thing):
            self.thing = thing
        def __enter__(self):
            return self.thing
        def __exit__(self, *exc_info):
            self.thing.close()

    上下文管理器会将包装的对象赋值给 as 子句的 target 变量,同时保证打开的对象在 with-body 执行完后会关闭掉。closing 上下文管理器包装起来的对象必须提供 close() 方法的定义,否则执行时会报 AttributeError 错误。

    自定义支持 closing 的对象
    class ClosingDemo(object):
        def __init__(self):
            self.acquire()
        def acquire(self):
            print 'Acquire resources.'
        def free(self):
            print 'Clean up any resources acquired.'
        def close(self):
            self.free()
     
    with closing(ClosingDemo()):
        print 'Using resources'

    结果输出如下:

    自定义 closing 对象的输出结果
    Acquire resources.
    Using resources
    Clean up any resources acquired.
    closing 适用于提供了 close() 实现的对象,比如网络连接、数据库连接等,也可以在自定义类时通过接口 close() 来执行所需要的资源“清理”工作。
     
  • 相关阅读:
    你的系统需要做系统集成测试么?
    测试驱动 ASP.NET MVC 和构建可测试 ASP.NET MVC 应用程序
    RikMigrations 或 Migrator.NET 进行自动化的数据库升级
    单元测试
    C#反射
    J2EE--Struts2基础开发
    Dynamics CRM 客户端的插件调试
    于快速创建 IEqualityComparer<T> 实例的类 Equality<T>
    ToolBox Analysis & Design
    实现$.fn.extend 和$.extend函数
  • 原文地址:https://www.cnblogs.com/menkeyi/p/7130978.html
Copyright © 2011-2022 走看看