什么是上下文管理
with open('text.txt','r') as f:
lines=f.readlines()
打开文件的with操作是代码中很常见的操作,这就是一个简单的上下文管理,而with open('text.txt','r') as f
就是上下文表达式;其中open('text.txt','r')
是上下文管理器,f为资源对象(说白了就是一个实例化的类)。
如何实现上下文管理器
上下文管理器是基于上下文管理协议锁生成的,其中最重要的上下文管理协议就是__enter__()
以及__exit__()
方法,分别表示获取资源或之前应当完成的动作,以及释放资源之后的动作。如果需要自己实现一个上下文管理器,那么一定要实现__enter__()
以及__exit__()
方法
class MyContexManager:
def __init__(self, name):
self.name=name
def __enter__(self):
print('Context name is', self.name)
print('-'*50)
for i in range(3):
print('|'+' '*48+'|')
print('|'+' '*23+'^_^'+' '*22+'|')
for i in range(3):
print('|'+' '*48+'|')
print('-' * 50)
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print(exc_type, exc_val, exc_tb)
print('Exit!')
return True
def contexMethod(self, *args, **kwargs):
print(args, kwargs)
with MyContexManager('dsa') as m:
print(type(m))
m.contexMethod('print',' hello')
最终的运行结果
Context name is dsa
--------------------------------------------------
| |
| |
| |
| ^_^ |
| |
| |
| |
--------------------------------------------------
<class '__main__.MyContexManager'>
('print', ' hello') {}
None None None
Exit!
当代码没有跑出错误的时候,上下文管理器相当于以下执行顺序:
m=MyContexManager('dsa')
m.__enter__()
m.contexMethod()
m.__exit__(None, None, None)
然而,当上下文管理器的主题发生错误之后,会将exception type
,exception value
,exception traceback
三个关键参数传入__exit__()
,进行错误处理。如果错误正常处理了,则在__exit__()
返回True,否则返回False。这也就可以看出,资源管理器除了更加便捷优雅的操作资源外,还有一个大作用就是优雅的处理出现的错误。
如果在主要的逻辑代码当中出现大量的try...except...
的代码,势必会影响可读性,因而在__exit__()
当中处理会显得更为美观。当在__exit__()
中处理了错误之后并返回True,不会再抛出错误。
class MyContexManager:
def __init__(self, name):
self.name=name
def __enter__(self):
print('Context name is', self.name)
print('-'*50)
for i in range(3):
print('|'+' '*48+'|')
print('|'+' '*23+'^_^'+' '*22+'|')
for i in range(3):
print('|'+' '*48+'|')
print('-' * 50)
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is ZeroDivisionError:
print('Zero division error')
return True
# print(exc_type, exc_val, exc_tb)
print('Exit!')
def contexMethod(self, *args, **kwargs):
print(1/0)
print(args, kwargs)
with MyContexManager('dsa') as m:
print(type(m))
m.contexMethod('print',' hello')
使用contextlib实现下文管理器
使用contextlib当中的contextmanager对函数进行装饰可以快速构建一个上下文管理器,而不需要自己手动繁杂的实现一个类。但是contextmanager装饰的函数必须包含yield关键字,其实也就是装饰了一个生成器。
import contextlib
@contextlib.contextmanager
def open_func(file_name):
# __enter__方法
print('open file:', file_name, 'in __enter__')
file_handler = open(file_name, 'r')
# 【重点】:yield
yield file_handler
# __exit__方法
print('close file:', file_name, 'in __exit__')
file_handler.close()
return
with open_func('/Users/MING/mytest.txt') as file_in:
for line in file_in:
print(line)
在被装饰的生成器中(带有yield),而yield之前的代码,就相当于__enter__
里的内容。yield之后的代码,就相当于__exit__
里的内容。如果需要在装饰器当中捕捉错误,那么可以将yield
包装在try当中。
from contextlib import contextmanager
def FileRename(filename: str):
import shutil
base,root=filename.split('.')
shutil.copy(filename, base+'_copy.'+root)
return open(base+'_copy.'+root)
@contextmanager
def manager(filename=None)->str:
print('rename')
res=FileRename(filename)
try:
yield res.readline()
except Exception as ex:
print('There is error ', ex.__str__())
finally:
print('-----finally-----')
with manager('text.txt') as m:
z=1/0
print(m)