zoukankan      html  css  js  c++  java
  • Python上下文管理器(Context managers)

    上下文管理器(Context managers)

    上下文管理器允许你在有需要的时候,精确地分配和释放资源。

    使用上下文管理器最广泛的案例就是with语句了。
    想象下你有两个需要结对执行的相关操作,然后还要在它们中间放置一段代码。
    上下文管理器就是专门让你做这种事情的。举个例子:

    with open('some_file', 'w') as opened_file:
        opened_file.write('Hola!')
    

    上面这段代码打开了一个文件,往里面写入了一些数据,然后关闭该文件。如果在往文件写数据时发生异常,它也会尝试去关闭文件。上面那段代码与这一段是等价的:

    file = open('some_file', 'w')
    try:
        file.write('Hola!')
    finally:
        file.close()
    

    当与第一个例子对比时,我们可以看到,通过使用with,许多样板代码(boilerplate code)被消掉了。 这就是with语句的主要优势,它确保我们的文件会被关闭,而不用关注嵌套代码如何退出。

    上下文管理器的一个常见用例,是资源的加锁和解锁,以及关闭已打开的文件(就像我已经展示给你看的)。

    让我们看看如何来实现我们自己的上下文管理器。这会让我们更完全地理解在这些场景背后都发生着什么。

    基于类的实现

    一个上下文管理器的类,最起码要定义__enter____exit__方法。
    让我们来构造我们自己的开启文件的上下文管理器,并学习下基础知识。

    class File(object):
        def __init__(self, file_name, method):
            self.file_obj = open(file_name, method)
        def __enter__(self):
            return self.file_obj
        def __exit__(self, type, value, traceback):
            self.file_obj.close()
    

    通过定义__enter____exit__方法,我们可以在with语句里使用它。我们来试试:

    with File('demo.txt', 'w') as opened_file:
        opened_file.write('Hola!')
    

    我们的__exit__函数接受三个参数。这些参数对于每个上下文管理器类中的__exit__方法都是必须的。我们来谈谈在底层都发生了什么。

    1. with语句先暂存了File类的__exit__方法
    2. 然后它调用File类的__enter__方法
    3. __enter__方法打开文件并返回给with语句
    4. 打开的文件句柄被传递给opened_file参数
    5. 我们使用.write()来写文件
    6. with语句调用之前暂存的__exit__方法
    7. __exit__方法关闭了文件

    基于生成器的实现

    我们还可以用装饰器(decorators)和生成器(generators)来实现上下文管理器。
    Python有个contextlib模块专门用于这个目的。我们可以使用一个生成器函数来实现一个上下文管理器,而不是使用一个类。
    让我们看看一个基本的,没用的例子:

    from contextlib import contextmanager
    
    @contextmanager
    def open_file(name):
        f = open(name, 'w')
        yield f
        f.close()
    

    OK啦!这个实现方式看起来更加直观和简单。然而,这个方法需要关于生成器、yield和装饰器的一些知识。在这个例子中我们还没有捕捉可能产生的任何异常。它的工作方式和之前的方法大致相同。

    让我们小小地剖析下这个方法。

      1. Python解释器遇到了yield关键字。因为这个缘故它创建了一个生成器而不是一个普通的函数。

      2. 因为这个装饰器,contextmanager会被调用并传入函数名(open_file)作为参数。

      3. contextmanager函数返回一个以GeneratorContextManager对象封装过的生成器。

      4. 这个GeneratorContextManager被赋值给open_file函数,我们实际上是在调用GeneratorContextManager对象。

    那现在我们既然知道了所有这些,我们可以用这个新生成的上下文管理器了,像这样:

    with open_file('some_file') as f:
        f.write('hola!')
  • 相关阅读:
    C++11 指针成员与拷贝构造(浅拷贝与深拷贝)
    C++11 委派构造函数
    C++11 继承构造函数
    C++11 局部和匿名类型作模板实参
    C++11 外部模板
    C++11 函数模板的默认模板参数
    2D游戏新手引导点光源和类迷雾实现
    UVA 12293
    【算法】8 图文搭配诠释三种链表及其哨兵
    小米面试
  • 原文地址:https://www.cnblogs.com/xiao-xue-di/p/10116653.html
Copyright © 2011-2022 走看看