今天在地铁上又看了一遍Python的上下文管理器,自己好像也没有完整记录过,所以刚到单位,就想写一篇文章。
本文主要讲解:
- Python上下文管理的定义与作用;
- 上下文管理器的使用;
- 自定义上下文管理器;
- 上下文管理器装饰器
一、Python上下文管理的定义与作用
上下文管理器是Python2.5之后才出现的概念。上下文管理器规定了某个对象的使用范围,当进入或者离开了使用范围,都会有相应的一些调用,比如代码块开始时执行一些准备,代码块结束时结束一些操作。它更多的是用于资源的分配和释放上,即在开始时分配资源,结束时释放一些资源。比如在执行数据库查询时要建立连接,查询结束后要释放连接;写文件时要先打开文件,写结束后,要关闭文件等等。还有,就是资源的加锁和解锁,比如在使用多线程时,可能会用到加锁和解锁。
上下文管理器可以通过使用更可读、更精简的代码实现资源的分配与释放。
二、上下文管理器的使用
上下文管理器的一个常见例子,就是建立或关闭数据库连接。使用装饰器实现:
对于上下文管理器的使用,最常见的是使用with语句,with语句可构建资源的分配与释放的语法糖。先拿最常见的例子来说,即文件的打开与关闭。
正常语法:
即正常情况下,你要显示的打开和关闭文件。但如果你用with语句,就会更可读,且永远不会因为忘记关闭文件而担忧:
三、自定义上下文管理器
要实现一个自定义的上下文管理器,肯定要实现两个方法,一是进入对象范围时的准备工作,二是离开对象范围时的结束工作。
Python提供了两个类的方法分别实现上述功能:
- __enter__ 进入对象范围时(一般代码块开始)被调用;
- __exit__ 离开对象范围时(代码块结束)呗调用;
因此,一个Python类,只要实现了上述两种方法,就可以说是一个上下文管理器。
再拿一个例子看看,Flask源码:
上面的IdentityContext是一个类装饰器,实现了上下文管理器的功能。该类定义了__enter__和__exit__两个方法。
上面就实现了上下文管理器的功能,那为什么这里没使用with语句,就可以呢?
如果细心的话,可能会看到IdentityContext的__call__方法,它里面使用了with语法。
with self:语句开始就自动执行__enter__ 方法,with语句结束,就执行__exit__ 方法。
那这个__call__方法的作用是啥?
对象通过提供__call__方法可以模拟函数的行为,如果一个对象提供了该方法,就可以像函数一样使用它,也就是说x(arg1, arg2...) 等同于调用x.__call__(self, arg1, arg2) 。比如:
说完了__call__,再看内部语句,实际上是一个函数装饰器,并且调用自己。
对于异常的处理,如果发现异常,就会调用__exit__方法,如果我们想忽略异常,就直接让其返回True即可。
四、上下文管理器装饰器
除了上面要写__enter__和__exit方法,我们可以用标准库中的装饰器改写,即contextlib.contextmanager。
test_context函数中yield之前的代码类似__enter__方法;yield之后的代码类似于__exit__方法。