之前我们详细的讲解了Python中如何创建一个上下文管理器,我们通过实现__enter__和__exit__方法来实现一个上下文管理器,然后通过with语句来调用上下文管理器。
可是这种方法有一个弊端,那就是需要编写者对源码比较了解,如果我们要改动的代码不是我们自己所编写的怎么办?有没有一种办法将他人的代码改造之后,也能够使用with语句来调用呢?
答案是肯定的,今天我们就来说一说Python的高级语法之contextmanager装饰器。
首先我们回顾一下上篇文章最后的内容。我们定义了一个方法create_a,它是一个上下文管理器,我们对create_a可以使用with语句
class A:
def __enter__(self):
print('执行__enter__')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('执行__exit__')
return True
@staticmethod
def query():
print('执行query方法')
def create_a():
return A()
with create_a() as a:
a.query()
print('执行with语句')
执行结果如下所示:
执行__enter__
执行query方法
执行with语句
执行__exit__
如果我们不用__enter__和__exit__的方法,要怎样才能实现相同的效果呢?
有了contextmanager其实很简单,我们只需要将代码做如下改造即可
from contextlib import contextmanager
class A:
@staticmethod
def query():
print('执行query方法')
@contextmanager
def create_a():
print('执行__enter__方法')
yield A()
print('执行__exit__方法')
with create_a() as a:
a.query()
print('执行with语句')
输出结果和最开始使用__enter__ __exit__是一样的。
下面说下具体步骤:
我们需要从contextlib中导入contextmanager
将contextmanager装饰器打在create_a方法上
将我们需要执行的enter方法写在上方
将A的对象返回,这里需要注意的是,返回要用yield,不能用return
将我们需要执行的exit方法写在最下方
上方面的代码利用contextmanager在未更改源代码的情况下,将create_a变成了一个上下文管理器。能用使用with来调用create_a。
虽然我们实现了功能,但是这里有个缺陷,那就是没有处理在with语句中处理的异常,我们使用with语句的目的,就是为了能够在发生异常的时候依然能够执行__exit__方法。
我们先来看一下,在上面的代码中,如果with语句中发生了异常,代码会如何运行。
更改with中的调用,人为增加一个异常:
with create_a() as a:
number = 1 / 0
a.query()
print('执行with语句')
代码运行结果:
可以看到,在执行到__enter__方法后,直接抛出了异常,没有执行__exit__,那么要如何处理这种情况呢?也很简单,我们只需要在create_a中增加异常处理机制即可。
@contextmanager
def create_a():
try:
print('执行__enter__方法')
yield A()
except Exception:
print('捕捉到了异常')
finally:
print('执行__exit__方法')
再次执行代码,运行结果如下所示:
执行__enter__方法
捕捉到了异常
执行__exit__方法
这样,我们在使用with语句时,无论是否发生异常,都可以执行__exit__中的内容了。
python培训:http://www.baizhiedu.com/python2019