上下文管理器——with语句的实现

前言

with语句的使用给我们带来了很多的便利,最常用的可能就是关闭一个文件,释放一把锁。

既然with语句这么好用,那我也想让我自己写的代码也能够使用with语句,该怎么实现?

下面具体介绍怎样实现一个自己的with语句

使用类实现

要想使用with语句,那就要遵循with语句的使用规矩,也就是上下文管理器协议

这个协议提示我们要在类中去实现两个特殊方法,enter(self)和exit(self, exc_type, exc_val, exc_tb)

有了这两个方法,就可以使用with语句执行了,它的执行过程为先执行enter(self)方法,然后执行你添加在with下的代码,然后最后执行exit方法

如果在执行的过程中出现了异常,那么会自动执行exit方法,


class ErrorProduce:
    def __init__(self):
        pass

    def produce(self):
        raise RuntimeError('ERROR')

class MyWith:
    def __enter__(self):
        return ErrorProduce()

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type:
            print("有错")
            #print(exc_val)
            #return True
        else:
            print("完美执行")

with MyWith() as E:
    E.produce()

将上述代码运行,会先打印出“有错”,然后再打印出相关错误,然后程序由于错误终止,也就是说,在产生了错误后,还是执行了exit中的代码

如果将exit中的两个注释符号去掉的话,那么再次运行程序不会报错,但会将exc_val中的错误信息打印出来,这里相当于通过exit方法将错误抹除掉了,抹除方式为返回True,但也把with中抛出错误后的所有代码都跳过了。

使用函数实现

通过引入contextlib模块中的contextmanager装饰器,装饰函数使函数可以使用with语句


from contextlib import contextmanager

class ErrorProduce:
    def __init__(self):
        pass

    def produce(self):
        raise RuntimeError('ERROR')

@contextmanager
def my_with():
    try:
        yield ErrorProduce()
    except Exception as e:
        print(e)
     #raise
    finally:
        print('收尾')

with my_with() as E:
    E.produce()
    print('aa')

函数中使用了yield,那么它也就变成了生成器,通过yield来划分相当于类中的enter和exit,yield返回的值也就是可以用作as后的值

上段代码中我们发现使用了try,except,finally异常处理的语句,这些语句是必要的吗?

通过测试,如果没有这些异常处理,那么with语句还是可以执行的,这里的意思是可以使用with语句,不会报不能使用with的错误,但是使用后,只有with下的语句中没有抛出异常,那么yield语句后边的代码才会执行,一旦有异常抛出,仍然是程序运行终止。所以,一般还是要配合try,except,finally语句来构造。

还有一点要注意,使用这种方式,异常被except捕捉后,那么这个异常就失效了,一般的我们还是会需要在except中再次将捕捉的异常抛出,也就是在后面加上raise,因为我们使用with只是为了做好一个收尾,如关闭文件句柄。或者另一种方式,我们干脆就不写except语句,直接写finally语句了。


  • 文章中出现的棕色加粗的单词,是由于markdown-here将特殊方法两边的下划线转换产生的,原意为特殊方法
原文地址:https://www.cnblogs.com/sfencs-hcy/p/10125534.html