美文网首页程序员
Python上下文管理器和with块

Python上下文管理器和with块

作者: 董小贱 | 来源:发表于2019-08-14 15:12 被阅读35次

    python中with语句用起来很方便,那这后边的原理就是python中的上下文管理器。

    1.什么是上下文管理器

    上下文管理器用来定义/控制代码块得执行前得准备工作,以及程序执行后得收尾工作。比如文件读写前的打开,与读写后的关闭。 网络请求的建立与断开等等。
    下边拿requests.session() 为例:

    import requests
    with requests.session() as session:
        response_html = session.get('http://www.baidu.com').text
        print (response_html)
    

    为什么with语句块能控制网络的建立与释放,原因是实现了上下文管理协议(context management protocol),相关的两个魔法方法:
    __enter__(self): 不接收参数,定义上下文管理器在with语句块开头所执行的操作,其方法返回的值绑定到,/as 后边所定义的变量。
    __exit__(self, exc_type, exc_value, traceback):接收三个参数,exc_type 表示错误类型;exc_value 异常实例。有时会有参数传给异常构造方法,例如错误消息,这些参数可以使用exception_type获取。traceback:traceback对象

    session的上下文协议源码


    session源码

    2.创建上下文管理器的两种方式

    我们重新实现一个类似open()的类,只是在创建文件对象时和关闭文件对象时增加了提示。

    2.1 实现上下文协议
    class File(object):
        def __init__(self, file_path, mode):
            self.file_path = file_path
             self.mode = mode
    
        def __enter__(self):
            self.f = open(self.file_path, self.mode, encoding='utf-8')
            print(f"{self.file_path} is open")
            return self.f
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.f.close()
            print(f"{self.file_path} is closed")
    
    
    with File('test.txt', 'w') as f:
        f.write('董小贱')
    

    输出结果:

    test.txt is open
    test.txt is closed
    
    2.2 装饰器实现
    from contextlib import contextmanager
    
    @contextmanager
    def file(file_path, mode):
        f = open(file_path, mode, encoding='utf-8')
        print(f"{file_path} is open")
    
        yield f
        f.close()
        print(f"{file_path} is closed")
    
    with file('test.txt, 'w') as f:
        f.write('董小贱')
    

    输出结果:

    test.txt is open
    test.txt is closed
    

    在使用 @contextmanager 装饰的生成器中,yield 语句的作用是把函数的定义体分成两部 分:yield 语句前面的所有代码在 with 块开始时(即解释器调用 __enter__方法时)执行, yield 语句后面的代码在 with 块结束时(即调用 __exit__ 方法时)执行。

    其实这里有个问题,如果在with块中抛出了一个异常,Python 解释器会将其捕获, 然后在 file 函数的 yield 表达式里再次抛出,但是代码中没有处理异常的代码,所以函数会终止,导致文件不能被关闭。所以我们要在yield处处理异常。

    所以第二版:

    from contextlib import contextmanager
    
    @contextmanager
    def file(file_path, mode):
        f = open(file_path, mode, encoding='utf-8')
        print(f"{file_path} is open")
        try:
            yield f
        except Exception as e:
            print (e) # 处理异常的操作
        finally:
            f.close()
            print(f"{file_path} is closed")
    with file('24.txt', 'w') as f:
        f.write('董小贱')
    

    注:实现上下文管理协议,即可使用with块。需要注意的是在 @contextmanager 装饰器装饰的生成器中,yield 与迭代没有任何关系。

    上下文管理器一个很好的例子 原地文件读写管理器

    更多细节,请参考 《流畅的python》的第十五章节。

    相关文章

      网友评论

        本文标题:Python上下文管理器和with块

        本文链接:https://www.haomeiwen.com/subject/bdlijctx.html