Python自定义上下文管理器

什么是上下文

程序中所谓的上下文件就是指程序所执行的环境状态,或者说程序运行的情景。

上下文管理器定义

写代码时,我们希望把一些操作放到一个代码块中,这样在代码块中执行时就可以保持在某种运行状态,而当离开该代码块时就执行另一个操作,结束当前状态;
所以,简单来说,上下文管理器的目的就是规定对象的使用范围,如果超出范围就采取“处理”,这一功能是在Python3.5之后引进的,它的优势在于可以使得你的代码更具可读性,且不容易出错。

常见的上下文管理器:with操作符

最常见的上下文管理器就是with语句了,python提供了with语句语法,来构建对资源的自动创建与自动释放,允许你在有需要的时候,精确地分配和释放资源。

使用方法:

需要使用try/finally来处理异常时的资源的正确关闭,方式如下:

fd = open('name.txt')
try:
    for line in fd:
        print(line)
finally:
    fd.close()
  
#而with语句可以更优雅简洁的实现资源的管理,语法结构如下:
#使用with语句代替try/finally:
with open('name.txt') as f:
    for line in f:
        print(line)

这样就不需要代码层面上显式的f.close()资源了。
with后的expression是一个上下文管理器,只有正确实现了上下文管理才可以使用with语句。
上下文管理器需要通过实现__enter__和__exit__两个函数来完成,with语句执行过程:
1.生成上下文管理器expression
2.执行expression的__enter__(),并将返回的变量赋值给[as var]中的var
3.执行with-block代码块
4.执行expression的__exit__(),进行资源清理工作

使用with的优点:

避免了琐碎操作:通过使用with,许多样板代码可以被消掉
避免了遗忘步骤:因此不用关注嵌套代码如何退出,又能确保我们的文件会被关闭

实现自定义上下文管理器

实现了上下文协议的函数/对象即为上下文管理器,上下文管理协议则是由enter和exit构成
要实现上下文管理器可以有两种方式,一种为基于类的实现,一种为基于生成器的实现。

基于类的实现:

基于类的实现需要实现两个方法:enter()和_exit_()
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_()方法的对象都可以用于上下文管理器,
#通过定义enter和exit方法,我们可以在with语句里使用它
with File('demo.txt', 'w') as opened_file:
    opened_file.write('HO!')

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

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

处理异常

如果发生异常,Python会将异常的type,value和traceback传递给exit方法
尝试访问文件对象的一个不支持的方法

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

exit方法返回的是None(如果没有return语句那么方法会返回None)。因此,with语句抛出了那个异常。

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
AttributeError: 'file' object has no attribute 'undefined_function'

当异常发生时,with语句会采取下面4步。

  1. 它把异常的type,value和traceback传递给exit方法。
  2. 它让exit方法来处理异常
  3. 如果exit返回的是True,那么这个异常就被优雅地处理了。
  4. 如果exit返回的是True以外的任何东西,那么这个异常将被with语句抛出
    在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):
        print("Exception has been handled")
        self.file_obj.close()
        return True

with File('demo.txt', 'w') as opened_file:
    opened_file.undefined_function()

exit方法返回了True,因此没有异常会被with语句抛出

基于生成器的实现:

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

from contextlib import contextmanager

@contextmanager
def file_open(path):
    try:
        f_obj = open(path,"w")
        yield f_obj
    except OSError:
        print("We had an error!")
    finally:
        print("Closing file")
        f_obj.close()

if __name__ == "__main__":
    with file_open("test.txt") as fobj:
        fobj.write("Testing context managers")

在这里,我们从contextlib模块中引入contextmanager,然后装饰我们所定义的file_open函数。这就允许我们使用Python的with语句来调用file_open函数。在函数中,我们打开文件,然后通过yield,将其传递出去,最终主调函数可以使用它。
一旦with语句结束,控制就会返回给file_open函数,它继续执行yield语句后面的代码。这个最终会执行finally语句–关闭文件。如果我们在打开文件时遇到了OSError错误,它就会被捕获,最终finally语句依然会关闭文件句柄。

发布了26 篇原创文章 · 获赞 2 · 访问量 534

猜你喜欢

转载自blog.csdn.net/weixin_42741880/article/details/104771933
今日推荐