写点什么

python 小知识 - 什么是上下文管理

作者:AIWeker
  • 2022 年 7 月 19 日
  • 本文字数:1801 字

    阅读完需:约 6 分钟

python小知识-什么是上下文管理

python 中进行文件操作,经常被建议使用with open的方式,形如:


with open('./dec.py', 'r') as f:    for line in f.readlines():        print(line)
复制代码


why? 这背后的原因是什么?


直观的解释是:这样的好处文件操作是 io 操作,操作完成之后,需要关闭 close,避免文件一直被占用打开。


与之对比另一种写法是:


f = open('./dec.py', 'r')for line in f.readlines():    print(line)f.close()
复制代码


两种写法对比可以知道:with 方式隐式的帮我们做了资源的释放操作,而这个可能是我们可能遗忘的。


那 with 是什么?有没有其他的作用?就是今天的主题。

1.with 上下文管理

with 是一种 python 上下文管理方式;什么是上下文(context)


  • context 是指程序运行所处于的环境和自身的状态(如数据)

  • 比如上面提到的文件操作,包含了文件打开获取文件操作的句柄权限(获得资源)、处理文件信息、关闭文件(释放资源);这些都是可以认为是文件的操作的环境和状态,也就是一个上下文(context 上下文这个译文有点怪怪,可以理解成一个容器内操作一个过程)


所以 with 上下文管理就是用在某种资源的创建,使用和回收以及异常处理过程中。语法结构如下:


with context_expression [as target(s)]:    with-body
复制代码


结合文件操作来说:


  • context_expression:获取资源,获取文件流

  • as target(s): 是可选的,可以认为资源变量

  • with-body: 具体的文件操作

2.如何构建自己的上下文管理

with 内部是如何实现的? 从上下文管理的概念可以知道:一个是获取资源,一个是释放资源。


与之对应的,只要实现了下面两个协议的都可以用 with 来进行上下文管理


  • enter: 实现获取资源

  • exit: 来实现释放资源的操作


我们来看下具体的例子:


import tracebackclass MyWith():    def __enter__(self):        print('enter the resource')        return self
def __exit__(self, exc_type, exc_val, exc_tb): print(exc_type, exc_val, exc_tb) print('release the resource') print(str(traceback.print_exc())) # 返回TRUE表示程序已经捕获到异常并处理 return True def with_body(self): print('this is with body') with MyWith() as res: res.with_body()
# enter the resource# this is with body# None None None# release the resource# None
复制代码


可以看出 __exit__包含了with-body的异常控制的处理和捕捉,返回 TRUE 表示程序已经捕获到异常并处理。所以即使你在函数with_body中出现异常情况,程序也会正常执行,除非你在__exit__直接抛出异常。


可以还原下 open 的写法:


import tracebackclass MyOpen():    def __init__(self, file_name, mode):        self.file_name = file_name        self.mode = mode        self.file = None
def __enter__(self): print('enter the resource') self.file = open(self.file_name, self.mode) return self.file
def __exit__(self, exc_type, exc_val, exc_tb): print(exc_type, exc_val, exc_tb) print('release the resource') print(str(traceback.print_exc()))
if self.file: self.file.close() with MyOpen('./dec.py', 'r' ) as f: for line in f.readlines(): print(line)
复制代码

3.用 contextlib 简化

对于需要上下文管理的场景,都要建立一个类,并实现 enter 和 __exit__接口来实现上下文管理;有没有更好的方式。


python 提供 contextlib 装饰器来简化上下文管理,具体如下:


import contextlib
@contextlib.contextmanagerdef simple_open(file_name, mode): # __enter__方法 print('open file:', file_name, 'in __enter__') file_handler = open(file_name, mode)
try: yield file_handler except Exception as exc: # deal with exception print('the exception was thrown') finally: print('close file:', file_name, 'in __exit__') file_handler.close()
return
with simple_open('./dec.py', 'r') as f: for line in f.readlines(): print(line)
复制代码


可以看出,


  • contextlib.contextmanager通过 yield 来代表资源,

  • yield 前面的获取资源

  • 通过 try...except 来进行异常处理,通过 finally 来做资源释放

发布于: 2022 年 07 月 19 日阅读数: 53
用户头像

AIWeker

关注

公众号:人工智能微客(aiweker) 2019.11.21 加入

人工智能微客(aiweker)长期跟踪和分享人工智能前沿技术、应用、领域知识,不定期的发布相关产品和应用,欢迎关注和转发

评论

发布
暂无评论
python小知识-什么是上下文管理_Python_AIWeker_InfoQ写作社区