上下文管理
本文主要讲解:
- Python上下文管理的定义与作用;
- 上下文管理器的使用;
- 自定义上下文管理器;
- 上下文管理器装饰器/python提供的上下文管理的模块;
一、Python上下文管理的定义与作用
上下文管理器是Python2.5之后才出现的概念。上下文管理器规定了某个对象的使用范围,当进入或者离开了使用范围,都会有相应的一些调用,比如代码块开始时执行一些准备,代码块结束时结束一些操作。它更多的是用于资源的分配和释放上,即在开始时分配资源,结束时释放一些资源。比如在执行数据库查询时要建立连接,查询结束后要释放连接;写文件时要先打开文件,写结束后,要关闭文件等等。还有,就是资源的加锁和解锁,比如在使用多线程时,可能会用到加锁和解锁。
二、上下文管理器的使用
上下文协同管理(__enter__和 __exit__)
# class Foo: # def __init__(self,name): # self.name = name # def __enter__(self): # print('执行enter',self) # return self # def __exit__(self, exc_type, exc_val, exc_tb): # print('执行exit') # print(exc_type) # print(exc_val) # print(exc_tb) # # with Foo('a.txt') as f: # 相当于f = obj.__enter__() ## with时触发enter # print('------') # print('=====') # print('000000') ### 上面的代码块执行完毕时触发exit ## 没有异常时,with 下的代码块运行完毕,自动触发exit,exc_type, exc_val, exc_tb三个参数均为None # class Foo: # def __init__(self,name): # self.name = name # def __enter__(self): # print('执行enter',self) # return self # def __exit__(self, exc_type, exc_val, exc_tb): # print('执行exit') # print(exc_type) # print(exc_val) # print(exc_tb) # # with Foo('a.txt') as f: # 相当于f = obj.__enter__() ## with时触发enter # print('------') # print(sdhf) ## 异常 # print('=====') # print('000000') ## 有异常时,到异常那句代码,直接触发、__exit__,此时三个参数为 # exc_type <class 'NameError'>; # exc_val name 'sdhf' is not defined ### exc_tb <traceback object at 0x0000000002203148> class Foo: def __init__(self,name): self.name = name def __enter__(self): print('执行enter',self) return self def __exit__(self, exc_type, exc_val, exc_tb): print('执行exit') print(exc_type) print(exc_val) print(exc_tb) return True with Foo('a.txt') as f: # 相当于f = obj.__enter__() ## with时触发enter print('------') print(sdhf) ## 异常 print('=====') print('000000') ## 有异常,但是exit返回值为True时,会吞掉异常,遇到异常那句代码,执行exit,然后开始执行with外面的代码 # 好处 #1.使用with语句的目的就是把代码放在with中执行,with结束后,自动完成清理工作,无需手动干预 #2.在需要管理一些资源,比如文件,网络连接,和锁的编程环境中,可以在__exit__中定制自动释放资源的机制。
三 contextlib模块
Python中还有一个contextlib模块提供一些简便的上下文管理器功能。
CLOSING()方法
如果说with语句块在退出时会自动调用”__exit__()”方法的话,那用了”contextlib.closing()”的with语句块则在退出时会自动调用”close()”方法。看一下示例:
程序运行后,会打印出
Open Resource Close Resource
说明Resource类创建的对象被赋给了as关键字后面的变量r,而with语句块退出时,自动调用了”r.close()”方法。
CONTEXTMANAGER装饰器
“@contextlib.contextmanager”是一个装饰器,由它修饰的方法会有两部分构成,中间由yield关键字分开。由此方法创建的上下文管理器,在代码块执行前会先执行yield上面的语句;在代码块执行后会再执行yield下面的语句。看个例子比较容易明白:
这个”timeit()”方法实现了一个计时器,它会计算由他生成的with语句块执行时间。可以看出,yield上面的语句就如同之间介绍过的”__enter__()”方法,而yield下面的语句就如同”__exit__()”方法。而yield部分就是with语句块中的代码。如果yield后面带参数的话,我们就可以用as关键字赋值给后面的变量,比如上例:
需要注意的是,”@contextlib.contextmanager”不像之前介绍的”__exit__()”方法,遇到异常也会执行。也就是with语句块抛出异常的话,yield后面的代码将不会被执行。所以,必要时你需要对yield语句使用”try-finally”。